aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build2/algorithm2
-rw-r--r--build2/algorithm.cxx9
-rw-r--r--build2/algorithm.ixx11
-rw-r--r--build2/b.cxx8
-rw-r--r--build2/bin/rule.cxx4
-rw-r--r--build2/bin/target.cxx64
-rw-r--r--build2/cli/rule.cxx10
-rw-r--r--build2/cli/target.cxx14
-rw-r--r--build2/config/operation.cxx8
-rw-r--r--build2/context6
-rw-r--r--build2/context.cxx29
-rw-r--r--build2/cxx/compile.cxx20
-rw-r--r--build2/cxx/link.cxx28
-rw-r--r--build2/dist/operation.cxx41
-rw-r--r--build2/dump.cxx35
-rw-r--r--build2/file.cxx69
-rw-r--r--build2/operation.cxx7
-rw-r--r--build2/parser.cxx73
-rw-r--r--build2/prerequisite37
-rw-r--r--build2/prerequisite.cxx5
-rw-r--r--build2/scope62
-rw-r--r--build2/scope.cxx161
-rw-r--r--build2/search8
-rw-r--r--build2/search.cxx110
-rw-r--r--build2/target95
-rw-r--r--build2/target-key14
-rw-r--r--build2/target-type8
-rw-r--r--build2/target.cxx82
-rw-r--r--build2/test/rule.cxx2
29 files changed, 544 insertions, 478 deletions
diff --git a/build2/algorithm b/build2/algorithm
index 06a5c01..19b3971 100644
--- a/build2/algorithm
+++ b/build2/algorithm
@@ -42,6 +42,7 @@ namespace build2
target&
search (const target_type& type,
const dir_path& dir,
+ const dir_path& out,
const string& name,
const string* ext,
scope*);
@@ -51,6 +52,7 @@ namespace build2
template <typename T>
T&
search (const dir_path& dir,
+ const dir_path& out,
const string& name,
const string* ext,
scope*);
diff --git a/build2/algorithm.cxx b/build2/algorithm.cxx
index 80325ed..ce25acf 100644
--- a/build2/algorithm.cxx
+++ b/build2/algorithm.cxx
@@ -44,7 +44,10 @@ namespace build2
n.dir.normalize ();
- return search (*tt, move (n.dir), move (n.value), e, &s);
+ // @@ OUT: for now we assume the prerequisite's out is undetermined.
+ // Would need to pass a pair of names.
+ //
+ return search (*tt, n.dir, dir_path (), n.value, e, &s);
}
pair<const rule*, match_result>
@@ -323,7 +326,9 @@ namespace build2
l6 ([&]{trace << "for " << t;});
- fsdir* r (&search<fsdir> (d, string (), nullptr, &s));
+ // Target in the out tree, so out directory is empty.
+ //
+ fsdir* r (&search<fsdir> (d, dir_path (), string (), nullptr, &s));
match (a, *r);
t.prerequisite_targets.emplace_back (r);
return r;
diff --git a/build2/algorithm.ixx b/build2/algorithm.ixx
index 3f0bab9..3930412 100644
--- a/build2/algorithm.ixx
+++ b/build2/algorithm.ixx
@@ -21,29 +21,32 @@ namespace build2
search (const target_type& t, const prerequisite_key& k)
{
return search (
- prerequisite_key
- {k.proj, {&t, k.tk.dir, k.tk.name, k.tk.ext}, k.scope});
+ prerequisite_key {
+ k.proj, {&t, k.tk.dir, k.tk.out, k.tk.name, k.tk.ext}, k.scope});
}
inline target&
search (const target_type& type,
const dir_path& dir,
+ const dir_path& out,
const string& name,
const string* ext,
scope* scope)
{
return search (
- prerequisite_key {nullptr, {&type, &dir, &name, ext}, scope});
+ prerequisite_key {nullptr, {&type, &dir, &out, &name, ext}, scope});
}
template <typename T>
inline T&
search (const dir_path& dir,
+ const dir_path& out,
const string& name,
const string* ext,
scope* scope)
{
- return static_cast<T&> (search (T::static_type, dir, name, ext, scope));
+ return static_cast<T&> (
+ search (T::static_type, dir, out, name, ext, scope));
}
pair<const rule*, match_result>
diff --git a/build2/b.cxx b/build2/b.cxx
index 7ebbab1..dcf2cfe 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -876,7 +876,13 @@ main (int argc, char* argv[])
d.normalize ();
- mif->search (rs, target_key {ti, &d, &tn.value, e}, l, tgs);
+ // Figure out if this target is in the src tree.
+ //
+ dir_path out (ts.out_base != ts.src_base && d.sub (ts.src_base)
+ ? out_src (d, ts.out_base, ts.src_base)
+ : dir_path ());
+
+ mif->search (rs, target_key {ti, &d, &out, &tn.value, e}, l, tgs);
}
}
diff --git a/build2/bin/rule.cxx b/build2/bin/rule.cxx
index b5d5ca6..072e931 100644
--- a/build2/bin/rule.cxx
+++ b/build2/bin/rule.cxx
@@ -66,7 +66,7 @@ namespace build2
if (ar)
{
if (t.a == nullptr)
- t.a = &search<liba> (t.dir, t.name, t.ext, nullptr);
+ t.a = &search<liba> (t.dir, t.out, t.name, nullptr, nullptr);
match_only (a, *t.a);
}
@@ -74,7 +74,7 @@ namespace build2
if (so)
{
if (t.so == nullptr)
- t.so = &search<libso> (t.dir, t.name, t.ext, nullptr);
+ t.so = &search<libso> (t.dir, t.out, t.name, nullptr, nullptr);
match_only (a, *t.so);
}
diff --git a/build2/bin/target.cxx b/build2/bin/target.cxx
index ed36f80..b0ce6d5 100644
--- a/build2/bin/target.cxx
+++ b/build2/bin/target.cxx
@@ -13,10 +13,14 @@ namespace build2
constexpr const char ext_var[] = "extension";
static target*
- obja_factory (const target_type&, dir_path d, string n, const string* e)
+ obja_factory (const target_type&,
+ dir_path dir,
+ dir_path out,
+ string n,
+ const string* e)
{
- obj* o (targets.find<obj> (d, n));
- obja* a (new obja (move (d), move (n), e));
+ obj* o (targets.find<obj> (dir, out, n));
+ obja* a (new obja (move (dir), move (out), move (n), e));
if ((a->group = o))
o->a = a;
@@ -36,10 +40,14 @@ namespace build2
};
static target*
- objso_factory (const target_type&, dir_path d, string n, const string* e)
+ objso_factory (const target_type&,
+ dir_path dir,
+ dir_path out,
+ string n,
+ const string* e)
{
- obj* o (targets.find<obj> (d, n));
- objso* so (new objso (move (d), move (n), e));
+ obj* o (targets.find<obj> (dir, out, n));
+ objso* so (new objso (move (dir), move (out), move (n), e));
if ((so->group = o))
o->so = so;
@@ -59,11 +67,15 @@ namespace build2
};
static target*
- obj_factory (const target_type&, dir_path d, string n, const string* e)
+ obj_factory (const target_type&,
+ dir_path dir,
+ dir_path out,
+ string n,
+ const string* e)
{
- obja* a (targets.find<obja> (d, n));
- objso* so (targets.find<objso> (d, n));
- obj* o (new obj (move (d), move (n), e));
+ obja* a (targets.find<obja> (dir, out, n));
+ objso* so (targets.find<objso> (dir, out, n));
+ obj* o (new obj (move (dir), move (out), move (n), e));
if ((o->a = a))
a->group = o;
@@ -108,12 +120,16 @@ namespace build2
};
static target*
- liba_factory (const target_type& t, dir_path d, string n, const string* e)
+ liba_factory (const target_type& t,
+ dir_path d,
+ dir_path o,
+ string n,
+ const string* e)
{
// Only link-up to the group if the types match exactly.
//
- lib* l (t == liba::static_type ? targets.find<lib> (d, n) : nullptr);
- liba* a (new liba (move (d), move (n), e));
+ lib* l (t == liba::static_type ? targets.find<lib> (d, o, n) : nullptr);
+ liba* a (new liba (move (d), move (o), move (n), e));
if ((a->group = l))
l->a = a;
@@ -145,12 +161,16 @@ namespace build2
};
static target*
- libso_factory (const target_type& t, dir_path d, string n, const string* e)
+ libso_factory (const target_type& t,
+ dir_path d,
+ dir_path o,
+ string n,
+ const string* e)
{
// Only link-up to the group if the types match exactly.
//
- lib* l (t == libso::static_type ? targets.find<lib> (d, n) : nullptr);
- libso* so (new libso (move (d), move (n), e));
+ lib* l (t == libso::static_type ? targets.find<lib> (d, o, n) : nullptr);
+ libso* so (new libso (move (d), move (o), move (n), e));
if ((so->group = l))
l->so = so;
@@ -179,11 +199,15 @@ namespace build2
}
static target*
- lib_factory (const target_type&, dir_path d, string n, const string* e)
+ lib_factory (const target_type&,
+ dir_path d,
+ dir_path o,
+ string n,
+ const string* e)
{
- liba* a (targets.find<liba> (d, n));
- libso* so (targets.find<libso> (d, n));
- lib* l (new lib (move (d), move (n), e));
+ liba* a (targets.find<liba> (d, o, n));
+ libso* so (targets.find<libso> (d, o, n));
+ lib* l (new lib (move (d), move (o), move (n), e));
if ((l->a = a))
a->group = l;
diff --git a/build2/cli/rule.cxx b/build2/cli/rule.cxx
index ece5424..65e940b 100644
--- a/build2/cli/rule.cxx
+++ b/build2/cli/rule.cxx
@@ -87,15 +87,15 @@ namespace build2
//
if (t.h == nullptr)
{
- t.h = &search<cxx::hxx> (t.dir, t.name, nullptr, nullptr);
+ t.h = &search<cxx::hxx> (t.dir, t.out, t.name, nullptr, nullptr);
t.h->group = &t;
- t.c = &search<cxx::cxx> (t.dir, t.name, nullptr, nullptr);
+ t.c = &search<cxx::cxx> (t.dir, t.out, t.name, nullptr, nullptr);
t.c->group = &t;
if (!find_option ("--suppress-inline", t, "cli.options"))
{
- t.i = &search<cxx::ixx> (t.dir, t.name, nullptr, nullptr);
+ t.i = &search<cxx::ixx> (t.dir, t.out, t.name, nullptr, nullptr);
t.i->group = &t;
}
}
@@ -116,7 +116,7 @@ namespace build2
// Then check if there is a corresponding cli.cxx{} group.
//
- cli_cxx* g (targets.find<cli_cxx> (t.dir, t.name));
+ cli_cxx* g (targets.find<cli_cxx> (t.dir, t.out, t.name));
// If not or if it has no prerequisites (happens when we use it to
// set cli.options) and this target has a cli{} prerequisite, then
@@ -133,7 +133,7 @@ namespace build2
if (match_stem (t.name, p.name ()))
{
if (g == nullptr)
- g = &targets.insert<cli_cxx> (t.dir, t.name, trace);
+ g = &targets.insert<cli_cxx> (t.dir, t.out, t.name, trace);
g->prerequisites.emplace_back (p.as_prerequisite (trace));
}
diff --git a/build2/cli/target.cxx b/build2/cli/target.cxx
index 44d1a9a..52ddd17 100644
--- a/build2/cli/target.cxx
+++ b/build2/cli/target.cxx
@@ -49,7 +49,11 @@ namespace build2
}
static target*
- cli_cxx_factory (const target_type&, dir_path d, string n, const string* e)
+ cli_cxx_factory (const target_type&,
+ dir_path d,
+ dir_path o,
+ string n,
+ const string* e)
{
tracer trace ("cli::cli_cxx_factory");
@@ -58,11 +62,11 @@ namespace build2
// src_base if the buildfile mentions some of them explicitly
// as prerequisites.
//
- targets.insert<cxx::hxx> (d, n, trace);
- targets.insert<cxx::cxx> (d, n, trace);
- targets.insert<cxx::ixx> (d, n, trace);
+ targets.insert<cxx::hxx> (d, o, n, trace);
+ targets.insert<cxx::cxx> (d, o, n, trace);
+ targets.insert<cxx::ixx> (d, o, n, trace);
- return new cli_cxx (move (d), move (n), e);
+ return new cli_cxx (move (d), move (o), move (n), e);
}
const target_type cli_cxx::static_type
diff --git a/build2/config/operation.cxx b/build2/config/operation.cxx
index 8b0bb3d..1c395d2 100644
--- a/build2/config/operation.cxx
+++ b/build2/config/operation.cxx
@@ -550,8 +550,12 @@ namespace build2
// root in diagnostics. Not very clean but seems harmless.
//
target& t (
- targets.insert (
- dir::static_type, root.out_path (), "", nullptr, trace).first);
+ targets.insert (dir::static_type,
+ root.out_path (),
+ dir_path (), // Out tree.
+ "",
+ nullptr,
+ trace).first);
if (!quiet)
info << diag_done (a, t);
diff --git a/build2/context b/build2/context
index ed2b0ab..bdbdc80 100644
--- a/build2/context
+++ b/build2/context
@@ -114,21 +114,21 @@ namespace build2
rmdir_r (const dir_path&);
// Return the src/out directory corresponding to the given out/src. The
- // passed directory should be a sub-directory of out/src_root.
+ // passed directory should be a sub-directory of out/src_base.
//
dir_path
src_out (const dir_path& out, scope&);
dir_path
src_out (const dir_path& out,
- const dir_path& out_root, const dir_path& src_root);
+ const dir_path& out_base, const dir_path& src_base);
dir_path
out_src (const dir_path& src, scope&);
dir_path
out_src (const dir_path& src,
- const dir_path& out_root, const dir_path& src_root);
+ const dir_path& out_base, const dir_path& src_base);
// If possible and beneficial, translate an absolute, normalized path
// into relative to the relative_base directory, which is normally
diff --git a/build2/context.cxx b/build2/context.cxx
index a36b2b6..a27d4ff 100644
--- a/build2/context.cxx
+++ b/build2/context.cxx
@@ -77,9 +77,9 @@ namespace build2
// <build2/path-map> for details.
//
{
- auto i (scopes.insert (dir_path ("/"), nullptr, true, false));
- global_scope = i->second;
- global_scope->out_path_ = global_scope->src_path_ = &i->first;
+ auto i (scopes.insert (dir_path ("/"), false));
+ global_scope = &i->second;
+ global_scope->out_path_ = &i->first;
}
scope& gs (*global_scope);
@@ -182,10 +182,7 @@ namespace build2
if (c == '!' || !dir.empty ())
{
- scope& s (c == '!'
- ? gs
- : *scopes.insert (dir, nullptr, true, false)->second);
-
+ scope& s (c == '!' ? gs : scopes.insert (dir, false)->second);
auto p (s.vars.assign (*o));
if (!p.second)
@@ -412,31 +409,29 @@ namespace build2
dir_path
src_out (const dir_path& out, scope& s)
{
- scope& rs (*s.root_scope ());
- return src_out (out, rs.out_path (), rs.src_path ());
+ return src_out (out, s.out_path (), s.src_path ());
}
dir_path
out_src (const dir_path& src, scope& s)
{
- scope& rs (*s.root_scope ());
- return out_src (src, rs.out_path (), rs.src_path ());
+ return out_src (src, s.out_path (), s.src_path ());
}
dir_path
src_out (const dir_path& o,
- const dir_path& out_root, const dir_path& src_root)
+ const dir_path& out_base, const dir_path& src_base)
{
- assert (o.sub (out_root));
- return src_root / o.leaf (out_root);
+ assert (o.sub (out_base));
+ return src_base / o.leaf (out_base);
}
dir_path
out_src (const dir_path& s,
- const dir_path& out_root, const dir_path& src_root)
+ const dir_path& out_base, const dir_path& src_base)
{
- assert (s.sub (src_root));
- return out_root / s.leaf (src_root);
+ assert (s.sub (src_base));
+ return out_base / s.leaf (src_base);
}
// relative()
diff --git a/build2/cxx/compile.cxx b/build2/cxx/compile.cxx
index 914cd10..b7577d4 100644
--- a/build2/cxx/compile.cxx
+++ b/build2/cxx/compile.cxx
@@ -679,9 +679,19 @@ namespace build2
// purposes: it is only important for us to accurately determine
// target types for headers that could be auto-generated.
//
- scope& b (scopes.find (d));
- if (b.root_scope () != nullptr)
- tt = map_extension (b, n, *e);
+ // While at it also try to determine if this target is from the src
+ // or out tree of said project.
+ //
+ dir_path out;
+
+ scope& bs (scopes.find (d));
+ if (bs.root_scope () != nullptr)
+ {
+ tt = map_extension (bs, n, *e);
+
+ if (bs.out_path () != bs.src_path () && d.sub (bs.src_path ()))
+ out = out_src (d, bs);
+ }
// If it is outside any project, or the project doesn't have
// such an extension, assume it is a plain old C header.
@@ -691,8 +701,10 @@ namespace build2
// Find or insert target.
//
+ // @@ OPT: move d, out, n
+ //
path_target& pt (
- static_cast<path_target&> (search (*tt, d, n, e, &ds)));
+ static_cast<path_target&> (search (*tt, d, out, n, e, &ds)));
// Assign path.
//
diff --git a/build2/cxx/link.cxx b/build2/cxx/link.cxx
index e4154ab..555c094 100644
--- a/build2/cxx/link.cxx
+++ b/build2/cxx/link.cxx
@@ -304,7 +304,10 @@ namespace build2
// Enter the target. Note that because the search paths are
// normalized, the result is automatically normalized as well.
//
- a = &targets.insert<liba> (d, p.name, ae, trace);
+ // Note that this target is outside any project which we treat
+ // as out trees.
+ //
+ a = &targets.insert<liba> (d, dir_path (), p.name, ae, trace);
if (a->path ().empty ())
a->path (move (f));
@@ -322,7 +325,7 @@ namespace build2
if ((mt = file_mtime (f)) != timestamp_nonexistent)
{
- s = &targets.insert<libso> (d, p.name, se, trace);
+ s = &targets.insert<libso> (d, dir_path (), p.name, se, trace);
if (s->path ().empty ())
s->path (move (f));
@@ -345,7 +348,7 @@ namespace build2
{
// Enter the target group.
//
- lib& l (targets.insert<lib> (*pd, p.name, p.ext, trace));
+ lib& l (targets.insert<lib> (*pd, dir_path (), p.name, p.ext, trace));
// It should automatically link-up to the members we have found.
//
@@ -597,12 +600,12 @@ namespace build2
? obj::static_type
: (so ? objso::static_type : obja::static_type));
- // Come up with the obj*{} target. 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
- // c(xx){} directory is most likely under src_root, it is also
- // possible it is under out_root (e.g., generated source).
+ // Come up with the obj*{} target. The c(xx){} prerequisite directory
+ // can be relative (to the scope) or absolute. If it is relative, then
+ // use it as is. If absolute, then translate it to the corresponding
+ // directory under out_root. While the c(xx){} directory is most
+ // likely under src_root, it is also possible it is under out_root
+ // (e.g., generated source).
//
dir_path d;
{
@@ -621,7 +624,10 @@ namespace build2
}
}
- target& ot (search (o_type, d, *cp.tk.name, nullptr, cp.scope));
+ // obj*{} is always in the out tree.
+ //
+ target& ot (
+ search (o_type, d, dir_path (), *cp.tk.name, nullptr, cp.scope));
// If we are cleaning, check that this target is in the same or
// a subdirectory of our project root.
@@ -646,7 +652,7 @@ namespace build2
if (pt == nullptr)
pt = &search (so ? objso::static_type : obja::static_type,
- o.dir, o.name, o.ext, nullptr);
+ o.dir, o.out, o.name, o.ext, nullptr);
}
else
pt = &ot;
diff --git a/build2/dist/operation.cxx b/build2/dist/operation.cxx
index 9ae5773..aea957a 100644
--- a/build2/dist/operation.cxx
+++ b/build2/dist/operation.cxx
@@ -139,21 +139,30 @@ namespace build2
// ignored on the next step if the user explicitly marked them
// nodist.
//
- auto add_adhoc = [&src_root, &trace] (const dir_path& d, const char* f)
+ auto add_adhoc = [&trace] (scope& rs, const char* f)
+ {
+ path p (rs.src_path () / path (f));
+ if (file_exists (p))
{
- path p (d / path (f));
- if (file_exists (p))
- {
- const char* e (p.extension ());
- targets.insert<buildfile> (
- p.directory (),
- p.leaf ().base ().string (),
- &extension_pool.find (e == nullptr ? "" : e), // Specified.
- trace);
- }
- };
+ dir_path d (p.directory ());
- add_adhoc (src_root, "build/export.build");
+ // Figure out if we need out.
+ //
+ dir_path out (rs.src_path () != rs.out_path ()
+ ? out_src (d, rs)
+ : dir_path ());
+
+ const char* e (p.extension ());
+ targets.insert<buildfile> (
+ move (d),
+ move (out),
+ p.leaf ().base ().string (),
+ &extension_pool.find (e == nullptr ? "" : e), // Specified.
+ trace);
+ }
+ };
+
+ add_adhoc (*rs, "build/export.build");
// The same for subprojects that have been loaded.
//
@@ -168,12 +177,10 @@ namespace build2
if (nrs.out_path () != out_nroot) // This subproject not loaded.
continue;
- const dir_path& src_nroot (nrs.src_path ());
-
- if (!src_nroot.sub (src_root)) // Not a strong amalgamation.
+ if (!nrs.src_path ().sub (src_root)) // Not a strong amalgamation.
continue;
- add_adhoc (src_nroot, "build/export.build");
+ add_adhoc (nrs, "build/export.build");
}
}
diff --git a/build2/dump.cxx b/build2/dump.cxx
index 648d8dd..442340f 100644
--- a/build2/dump.cxx
+++ b/build2/dump.cxx
@@ -18,7 +18,7 @@ namespace build2
dump_variable (ostream& os,
const variable& var,
const lookup& org,
- scope& s,
+ const scope& s,
bool target)
{
os << var.name << " = ";
@@ -63,7 +63,7 @@ namespace build2
dump_variables (ostream& os,
string& ind,
const variable_map& vars,
- scope& s,
+ const scope& s,
bool target)
{
for (const auto& e: vars)
@@ -79,7 +79,7 @@ namespace build2
dump_variables (ostream& os,
string& ind,
const variable_type_map& vtm,
- scope& s)
+ const scope& s)
{
for (const auto& vt: vtm)
{
@@ -128,7 +128,11 @@ namespace build2
}
static void
- dump_target (ostream& os, string& ind, action a, const target& t, scope& s)
+ dump_target (ostream& os,
+ string& ind,
+ action a,
+ const target& t,
+ const scope& s)
{
// Print the target and its prerequisites relative to the scope. To achieve
// this we are going to temporarily lower the stream verbosity to level 1.
@@ -196,7 +200,7 @@ namespace build2
action a,
scope_map::const_iterator& i)
{
- scope& p (*i->second);
+ const scope& p (i->second);
const dir_path& d (i->first);
++i;
@@ -235,23 +239,8 @@ namespace build2
// Nested scopes of which we are an immediate parent.
//
- for (auto e (scopes.end ()); i != e && i->second->parent_scope () == &p;)
+ for (auto e (scopes.end ()); i != e && i->second.parent_scope () == &p;)
{
- // See what kind of scope entry this is. It can be:
- //
- // 1. Out-of-project scope.
- // 2. In-project out entry.
- // 3. In-project src entry.
- //
- // We want to print #2 and #3 as a single, unified scope.
- //
- scope& s (*i->second);
- if (s.src_path_ != s.out_path_ && s.src_path_ == &i->first)
- {
- ++i;
- continue;
- }
-
if (vb)
{
os << endl;
@@ -295,8 +284,8 @@ namespace build2
void
dump (action a)
{
- auto i (scopes.begin ());
- assert (i->second == global_scope);
+ auto i (scopes.cbegin ());
+ assert (&i->second == global_scope);
string ind;
ostream& os (*diag_stream);
diff --git a/build2/file.cxx b/build2/file.cxx
index 4b0795c..f5ce91c 100644
--- a/build2/file.cxx
+++ b/build2/file.cxx
@@ -131,8 +131,8 @@ namespace build2
scope&
create_root (const dir_path& out_root, const dir_path& src_root)
{
- auto i (scopes.insert (out_root, nullptr, true, true));
- scope& rs (*i->second);
+ auto i (scopes.insert (out_root, true));
+ scope& rs (i->second);
// Set out_path. src_path is set in setup_root() below.
//
@@ -199,14 +199,16 @@ namespace build2
void
setup_root (scope& s)
{
+ // The caller must have made sure src_root is set on this scope.
+ //
value& v (s.assign ("src_root"));
assert (v);
+ const dir_path& d (cast<dir_path> (v));
- // Register and set src_path.
- //
if (s.src_path_ == nullptr)
- s.src_path_ = &scopes.insert (
- cast<dir_path> (v), &s, false, true)->first;
+ s.src_path_ = &d;
+ else
+ assert (s.src_path_ == &d);
}
scope&
@@ -214,46 +216,35 @@ namespace build2
const dir_path& out_base,
const dir_path& src_base)
{
- scope& s (*i->second);
+ scope& s (i->second);
- // Set src/out_path. The key (i->first) can be either out_base
- // or src_base.
+ // Set src/out_base variables.
//
- if (s.out_path_ == nullptr)
- {
- s.out_path_ =
- i->first == out_base
- ? &i->first
- : &scopes.insert (out_base, &s, true, false)->first;
- }
+ value& ov (s.assign ("out_base"));
- if (s.src_path_ == nullptr)
- {
- s.src_path_ =
- i->first == src_base
- ? &i->first
- : &scopes.insert (src_base, &s, false, false)->first;
- }
+ if (!ov)
+ ov = out_base;
+ else
+ assert (cast<dir_path> (ov) == out_base);
- // Set src/out_base variables.
- //
- {
- value& v (s.assign ("out_base"));
+ value& sv (s.assign ("src_base"));
- if (!v)
- v = out_base;
- else
- assert (cast<dir_path> (v) == out_base);
- }
+ if (!sv)
+ sv = src_base;
+ else
+ assert (cast<dir_path> (sv) == src_base);
- {
- value& v (s.assign ("src_base"));
+ // Set src/out_path. The key (i->first) is out_base.
+ //
+ if (s.out_path_ == nullptr)
+ s.out_path_ = &i->first;
+ else
+ assert (*s.out_path_ == out_base);
- if (!v)
- v = src_base;
- else
- assert (cast<dir_path> (v) == src_base);
- }
+ if (s.src_path_ == nullptr)
+ s.src_path_ = &cast<dir_path> (sv);
+ else
+ assert (*s.src_path_ == src_base);
return s;
}
diff --git a/build2/operation.cxx b/build2/operation.cxx
index c5ba2fb..f8ce46f 100644
--- a/build2/operation.cxx
+++ b/build2/operation.cxx
@@ -54,11 +54,10 @@ namespace build2
//
load_root_pre (root);
- // Create the base scope. Note that its existence doesn't
- // mean it was already setup as a base scope; it can be the
- // same as root.
+ // Create the base scope. Note that its existence doesn't mean it was
+ // already setup as a base scope; it can be the same as root.
//
- auto i (scopes.insert (out_base, nullptr, true, false));
+ auto i (scopes.insert (out_base, false));
scope& base (setup_base (i, out_base, src_base));
// Load the buildfile unless it has already been loaded.
diff --git a/build2/parser.cxx b/build2/parser.cxx
index 80e744e..bd44f27 100644
--- a/build2/parser.cxx
+++ b/build2/parser.cxx
@@ -94,7 +94,12 @@ namespace build2
// Find or insert.
//
- p.target_ = &targets.insert (*ti, move (d), move (n.value), e, tr).first;
+ // @@ OUT: for now we assume the target is always in the out tree. The
+ // only way to specify an src prerequisite will be with the explicit
+ // @-syntax.
+ //
+ p.target_ = &targets.insert (
+ *ti, move (d), dir_path (), move (n.value), e, tr).first;
}
~enter_target ()
@@ -120,8 +125,6 @@ namespace build2
void parser::
parse_buildfile (istream& is, const path& p, scope& root, scope& base)
{
- enter_buildfile (p);
-
path_ = &p;
lexer l (is, *path_);
@@ -131,6 +134,8 @@ namespace build2
root_ = &root;
default_target_ = nullptr;
+ enter_buildfile (p); // Needs scope_.
+
token t (type::eos, false, 0, 0);
type tt;
next (t, tt);
@@ -512,11 +517,22 @@ namespace build2
// Find or insert.
//
+ // @@ OUT: for now we assume the prerequisite's out is
+ // undetermined. The only way to specify an src prerequisite
+ // will be with the explicit @-syntax.
+ //
+ // Perhaps use @file{foo} as a way to specify it is in the out
+ // tree, e.g., to suppress any src searches? The issue is what
+ // to use for such a special indicator. Also, one can easily and
+ // natually suppress any searches by specifying the absolute
+ // path.
+ //
prerequisite& p (
scope_->prerequisites.insert (
pn.proj,
*ti,
move (pn.dir),
+ dir_path (),
move (pn.value),
e,
*scope_,
@@ -622,8 +638,8 @@ namespace build2
// If the path is relative then use the src directory corresponding
// to the current directory scope.
//
- if (root_->src_path_ != nullptr && p.relative ())
- p = src_out (scope_->out_path (), *root_) / p;
+ if (scope_->src_path_ != nullptr && p.relative ())
+ p = scope_->src_path () / p;
p.normalize ();
@@ -1084,7 +1100,11 @@ namespace build2
}
static target*
- derived_factory (const target_type& t, dir_path d, string n, const string* e)
+ derived_factory (const target_type& t,
+ dir_path d,
+ dir_path o,
+ string n,
+ const string* e)
{
// Pass our type to the base factory so that it can detect that it is
// being called to construct a derived target. This can be used, for
@@ -1097,7 +1117,7 @@ namespace build2
const target_type* bt (t.base);
for (; bt->factory == &derived_factory; bt = bt->base) ;
- target* r (bt->factory (t, move (d), move (n), e));
+ target* r (bt->factory (t, move (d), move (o), move (n), e));
r->derived_type = &t;
return r;
}
@@ -2734,11 +2754,11 @@ namespace build2
{
tracer trace ("parser::switch_scope", &path_);
- // First, enter the scope into the map and see if it is in any
- // project. If it is not, then there is nothing else to do.
+ // First, enter the scope into the map and see if it is in any project. If
+ // it is not, then there is nothing else to do.
//
- auto i (scopes.insert (p, nullptr, true, false));
- scope_ = i->second;
+ auto i (scopes.insert (p, false));
+ scope_ = &i->second;
scope* rs (scope_->root_scope ());
if (rs == nullptr)
@@ -2791,6 +2811,7 @@ namespace build2
if (default_target_ == nullptr || // No targets in this buildfile.
targets.find (dir::static_type, // Explicit current dir target.
scope_->out_path (),
+ dir_path (), // Out tree target.
"",
nullptr,
trace) != targets.end ())
@@ -2801,17 +2822,18 @@ namespace build2
l5 ([&]{trace (t) << "creating current directory alias for " << dt;});
target& ct (
- targets.insert (
- dir::static_type, scope_->out_path (), "", nullptr, trace).first);
+ targets.insert (dir::static_type,
+ scope_->out_path (),
+ dir_path (),
+ "",
+ nullptr,
+ trace).first);
prerequisite& p (
scope_->prerequisites.insert (
nullptr,
- dt.type (),
- dt.dir,
- dt.name,
- dt.ext,
- *scope_, // Doesn't matter which scope since dir is absolute.
+ dt.key (),
+ *scope_, // Doesn't matter which scope since dir is absolute.
trace).first);
p.target = &dt;
@@ -2823,9 +2845,22 @@ namespace build2
{
tracer trace ("parser::enter_buildfile", &path_);
+ dir_path d (p.directory ());
+
+ // Figure out if we need out.
+ //
+ dir_path out;
+ if (scope_->src_path_ != nullptr &&
+ scope_->src_path () != scope_->out_path () &&
+ d.sub (scope_->src_path ()))
+ {
+ out = out_src (d, *scope_);
+ }
+
const char* e (p.extension ());
targets.insert<buildfile> (
- p.directory (),
+ move (d),
+ move (out),
p.leaf ().base ().string (),
&extension_pool.find (e == nullptr ? "" : e), // Always specified.
trace);
diff --git a/build2/prerequisite b/build2/prerequisite
index baaaf3f..f316fb5 100644
--- a/build2/prerequisite
+++ b/build2/prerequisite
@@ -27,8 +27,8 @@ namespace build2
typedef build2::scope scope_type;
mutable const string* proj; // Can be NULL, from project_name_pool.
- target_key tk;
- mutable scope_type* scope; // Can be NULL if tk.dir is absolute.
+ target_key tk; // .dir and .out can be relative.
+ mutable scope_type* scope; // Can be NULL if tk.dir is absolute.
};
inline bool
@@ -51,38 +51,44 @@ namespace build2
typedef build2::target_type target_type_type;
typedef build2::scope scope_type;
+ // Note that unlike targets, for prerequisites an empty out directory
+ // means undetermined rather than being definitely in the out tree.
+ //
+ const string* const proj; // NULL if not project-qualified.
+ const target_type_type& type;
+ const dir_path dir; // Normalized absolute or relative (to scope).
+ const dir_path out; // Empty, normalized absolute, or relative.
+ const string name;
+ const string* ext; // NULL if unspecified.
+ scope_type& scope;
+ 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 (const string* p,
const target_type_type& t,
dir_path d,
+ dir_path o,
string n,
const string* e,
scope_type& s)
: proj (p),
type (t),
dir (move (d)),
+ out (move (o)),
name (move (n)),
ext (e),
scope (s),
target (nullptr) {}
- public:
- const string* const proj; // NULL if not project-qualified.
- const target_type_type& type;
- const dir_path dir; // Normalized absolute or relative (to scope).
- const string name;
- const string* ext; // NULL if unspecified.
- scope_type& scope;
- target_type* target; // NULL if not yet resolved. Note that this should
- // always be the "primary target", not a member of
- // a target group.
-
// Note that the returned key "tracks" the prerequisite; that is, any
// updates to the prerequisite's members will be reflected in the key.
//
prerequisite_key
key () const
{
- return prerequisite_key {proj, {&type, &dir, &name, ext}, &scope};
+ return prerequisite_key {proj, {&type, &dir, &out, &name, ext}, &scope};
}
public:
@@ -113,6 +119,7 @@ namespace build2
insert (const string* proj,
const target_type&,
dir_path dir,
+ dir_path out,
string name,
const string* ext,
scope&,
@@ -121,7 +128,7 @@ namespace build2
pair<prerequisite&, bool>
insert (const string* proj, const target_key& tk, scope& s, tracer& t)
{
- return insert (proj, *tk.type, *tk.dir, *tk.name, tk.ext, s, t);
+ return insert (proj, *tk.type, *tk.dir, *tk.out, *tk.name, tk.ext, s, t);
}
};
}
diff --git a/build2/prerequisite.cxx b/build2/prerequisite.cxx
index e28d082..4adfc92 100644
--- a/build2/prerequisite.cxx
+++ b/build2/prerequisite.cxx
@@ -52,6 +52,7 @@ namespace build2
insert (const string* proj,
const target_type& tt,
dir_path dir,
+ dir_path out,
string name,
const string* ext,
scope& s,
@@ -62,11 +63,13 @@ namespace build2
// Find or insert.
//
- auto r (emplace (proj, tt, move (dir), move (name), ext, s));
+ auto r (emplace (proj, tt, move (dir), move (out), move (name), ext, s));
prerequisite& p (const_cast<prerequisite&> (*r.first));
// Update extension if the existing prerequisite has it unspecified.
//
+ // @@ Changing the key!
+ //
if (p.ext != ext)
{
l5 ([&]{
diff --git a/build2/scope b/build2/scope
index c1dcb91..ad9c3a0 100644
--- a/build2/scope
+++ b/build2/scope
@@ -27,16 +27,14 @@ namespace build2
public:
// Absolute and normalized.
//
- const dir_path&
- out_path () const {return *out_path_;}
+ const dir_path& out_path () const {return *out_path_;}
+ const dir_path& src_path () const {return *src_path_;}
- const dir_path&
- src_path () const {return *src_path_;}
-
- // These are pointers to the keys in scope_map.
+ // The first is a pointer to the key in scope_map. The second is a pointer
+ // to the src_root/base variable value, if any (i.e., it can be NULL).
//
- const dir_path* out_path_ {nullptr};
- const dir_path* src_path_ {nullptr};
+ const dir_path* out_path_ = nullptr;
+ const dir_path* src_path_ = nullptr;
bool
root () const {return root_ == this;}
@@ -230,22 +228,6 @@ namespace build2
public:
loaded_module_map modules; // Only on root scope.
- public:
- bool
- empty () const
- {
- return
- vars.empty () &&
- target_vars.empty () &&
- prerequisites.empty () &&
- meta_operations.empty () &&
- operations.empty () &&
- buildfiles.empty () &&
- target_types.empty () &&
- rules.empty () &&
- modules.empty ();
- }
-
private:
friend class scope_map;
friend class temp_scope;
@@ -284,27 +266,26 @@ namespace build2
}
};
- class scope_map
+ // Note that the scope map is only for paths from the out tree.
+ //
+ using scope_map_base = butl::dir_path_map<scope>;
+
+ class scope_map: public scope_map_base
{
public:
- using map_type = butl::dir_path_map<scope*>;
- using iterator = map_type::iterator;
- using const_iterator = map_type::const_iterator;
-
- // Note that we assume the first insertion into the map is that
- // of the global scope. If the passed scope pointer is not NULL,
- // then insert this scope instead of a new one.
+ // Note that we assume the first insertion into the map is always the
+ // global scope.
//
iterator
- insert (const dir_path&, scope*, bool parent, bool root);
+ insert (const dir_path&, bool root);
// Find the most qualified scope that encompasses this path.
//
scope&
- find (const dir_path&) const;
+ find (const dir_path&);
scope&
- find (const path& p) const
+ find (const path& p)
{
// Natural thing to do here would be to call find (p.directory ()).
// However, there could be a situation where the passed path is a
@@ -313,17 +294,6 @@ namespace build2
//
return find (dir_path (p.string ()));
}
-
- const_iterator begin () const {return map_.begin ();}
- const_iterator end () const {return map_.end ();}
-
- void
- clear ();
-
- ~scope_map () {clear ();}
-
- private:
- map_type map_;
};
extern scope_map scopes;
diff --git a/build2/scope.cxx b/build2/scope.cxx
index 1459687..70442f6 100644
--- a/build2/scope.cxx
+++ b/build2/scope.cxx
@@ -523,105 +523,70 @@ namespace build2
scope* global_scope;
auto scope_map::
- insert (const dir_path& k, scope* ns, bool parent, bool root) -> iterator
+ insert (const dir_path& k, bool root) -> iterator
{
- auto er (map_.emplace (k, nullptr));
- scope*& ps (er.first->second);
+ scope_map_base& m (*this);
+ auto er (m.emplace (k, scope ()));
+ scope& s (er.first->second);
+
+ // If this is a new scope, update the parent chain.
+ //
if (er.second)
- ps = ns == nullptr ? new scope : ns;
- else if (ns != nullptr && ps != ns)
{
- assert (ps->out_path_ == nullptr || ps->src_path_ == nullptr);
-
- if (!ps->empty ())
- fail << "attempt to replace non-empty scope " << k;
+ scope* p (nullptr);
- // Un-parent ourselves. We will becomes a new parent below,
- // if requested by the caller.
+ // Update scopes of which we are a new parent/root (unless this is the
+ // global scope). Also find our parent while at it.
//
- auto r (map_.find_prefix (k)); // The first entry is ourselves.
- for (++r.first; r.first != r.second; ++r.first)
- {
- scope& c (*r.first->second);
-
- if (c.parent_ == ps) // No intermediate parent.
- c.parent_ = ps->parent_;
- }
-
- delete ps;
- ps = ns;
- er.second = true;
- }
-
- scope& s (*ps);
-
- if (parent)
- {
- if (er.second)
+ if (m.size () > 1)
{
- scope* p (nullptr);
-
- // Update scopes of which we are a new parent/root (unless this
- // is the global scope). Also find our parent while at it.
+ // The first entry is ourselves.
//
- if (map_.size () > 1)
+ auto r (m.find_prefix (k));
+ for (++r.first; r.first != r.second; ++r.first)
{
- // The first entry is ourselves.
- //
- auto r (map_.find_prefix (k));
- for (++r.first; r.first != r.second; ++r.first)
- {
- scope& c (*r.first->second);
-
- // The child-parent relationship is based on the out hierarchy,
- // thus the extra check.
- //
- if (c.out_path_ != nullptr && !c.out_path_->sub (k))
- continue;
-
- // The first scope of which we are a parent is the least
- // (shortest) one which means there is no other scope
- // between it and our parent.
- //
- if (p == nullptr)
- p = c.parent_;
-
- if (root && c.root_ == p->root_) // No intermediate root.
- c.root_ = &s;
-
- if (p == c.parent_) // No intermediate parent.
- c.parent_ = &s;
- }
+ scope& c (r.first->second);
- // We couldn't get the parent from one of its old children
- // so we have to find it ourselves.
+ // The first scope of which we are a parent is the least (shortest)
+ // one which means there is no other scope between it and our
+ // parent.
//
if (p == nullptr)
- p = &find (k.directory ());
+ p = c.parent_;
+
+ if (root && c.root_ == p->root_) // No intermediate root.
+ c.root_ = &s;
+
+ if (p == c.parent_) // No intermediate parent.
+ c.parent_ = &s;
}
- s.parent_ = p;
- s.root_ = root ? &s : (p != nullptr ? p->root_ : nullptr);
- }
- else if (root && !s.root ())
- {
- // Upgrade to root scope.
+ // We couldn't get the parent from one of its old children so we have
+ // to find it ourselves.
//
- auto r (map_.find_prefix (k));
- for (++r.first; r.first != r.second; ++r.first)
- {
- scope& c (*r.first->second);
+ if (p == nullptr)
+ p = &find (k.directory ());
+ }
- if (c.root_ == s.root_) // No intermediate root.
- c.root_ = &s;
- }
+ s.parent_ = p;
+ s.root_ = root ? &s : (p != nullptr ? p->root_ : nullptr);
+ }
+ else if (root && !s.root ())
+ {
+ // Upgrade to root scope.
+ //
+ auto r (m.find_prefix (k));
+ for (++r.first; r.first != r.second; ++r.first)
+ {
+ scope& c (r.first->second);
- s.root_ = &s;
+ if (c.root_ == s.root_) // No intermediate root.
+ c.root_ = &s;
}
+
+ s.root_ = &s;
}
- else
- assert (s.parent_ != nullptr);
return er.first;
}
@@ -629,44 +594,26 @@ namespace build2
// Find the most qualified scope that encompasses this path.
//
scope& scope_map::
- find (const dir_path& k) const
+ find (const dir_path& k)
{
- // Normally we would have a scope for the full path so try
- // that before making any copies.
+ scope_map_base& m (*this);
+
+ // Normally we would have a scope for the full path so try that before
+ // making any copies.
//
- auto i (map_.find (k)), e (map_.end ());
+ auto i (m.find (k)), e (m.end ());
if (i != e)
- return *i->second;
+ return i->second;
for (dir_path d (k.directory ());; d = d.directory ())
{
- auto i (map_.find (d));
+ auto i (m.find (d));
if (i != e)
- return *i->second;
+ return i->second;
assert (!d.empty ()); // We should have the global scope.
}
}
-
- void scope_map::
- clear ()
- {
- for (auto& p: map_)
- {
- scope* s (p.second);
-
- if (s->out_path_ == &p.first)
- s->out_path_ = nullptr;
-
- if (s->src_path_ == &p.first)
- s->src_path_ = nullptr;
-
- if (s->out_path_ == nullptr && s->src_path_ == nullptr)
- delete s;
- }
-
- map_.clear ();
- }
}
diff --git a/build2/search b/build2/search
index df9b73f..70ae73a 100644
--- a/build2/search
+++ b/build2/search
@@ -18,10 +18,14 @@ namespace build2
target*
search_existing_target (const prerequisite_key&);
- // Search for an existing file in the specified list of search paths.
+ // Search for an existing file in the scope's src directory. Prerequisite
+ // directory should be relative.
+ //
+ // Originally the plan was to have a target-type specific variable that
+ // contains the search paths. But there wasn't any need for this yet.
//
target*
- search_existing_file (const prerequisite_key&, const dir_paths&);
+ search_existing_file (const prerequisite_key&);
// Create a new target in this prerequisite's scope.
//
diff --git a/build2/search.cxx b/build2/search.cxx
index 7e85dab..cea7a7e 100644
--- a/build2/search.cxx
+++ b/build2/search.cxx
@@ -23,7 +23,7 @@ namespace build2
const target_key& tk (pk.tk);
- // Look for an existing target in this directory scope.
+ // Look for an existing target in the prerequisite's scope.
//
dir_path d;
if (tk.dir->absolute ())
@@ -39,7 +39,23 @@ namespace build2
}
}
- auto i (targets.find (*tk.type, d, *tk.name, tk.ext, trace));
+ // Prerequisite's out directory can be on of the following:
+ //
+ // empty This means out is undetermined and we simply search for a
+ // target that is in the out tree which happens to be indicated
+ // by an empty value, so we can just pass this as is.
+ //
+ // absolute This is the "final" value that doesn't require any processing
+ // and we simply use it as is.
+ //
+ // relative The out directory was specified using @-syntax as relative (to
+ // the prerequisite's scope) and we need to complete it similar
+ // to how we complete the relative dir above. This is @@ OUT TODO.
+ //
+ // What if we have user-supplied out but it is in-src build. Shouldn't we
+ // drop it?
+ //
+ auto i (targets.find (*tk.type, d, *tk.out, *tk.name, tk.ext, trace));
if (i == targets.end ())
return 0;
@@ -51,7 +67,7 @@ namespace build2
}
target*
- search_existing_file (const prerequisite_key& cpk, const dir_paths& sp)
+ search_existing_file (const prerequisite_key& cpk)
{
tracer trace ("search_existing_file");
@@ -85,50 +101,73 @@ namespace build2
// Make a copy with the updated extension.
//
const prerequisite_key pk {
- cpk.proj, target_key {ctk.type, ctk.dir, ctk.name, ext}, cpk.scope};
+ cpk.proj, {ctk.type, ctk.dir, ctk.out, ctk.name, ext}, cpk.scope};
const target_key& tk (pk.tk);
- // Go over paths looking for a file.
+ // Check if there is a file.
//
- for (const dir_path& d: sp)
+ const dir_path& s (pk.scope->src_path ());
+ path f (s / *tk.dir / path (*tk.name));
+ f.normalize ();
+
+ if (!ext->empty ())
{
- path f (d / *tk.dir / path (*tk.name));
- f.normalize ();
+ f += '.';
+ f += *ext;
+ }
- if (!ext->empty ())
- {
- f += '.';
- f += *ext;
- }
+ timestamp mt (file_mtime (f));
- timestamp mt (file_mtime (f));
+ if (mt == timestamp_nonexistent)
+ {
+ l4 ([&]{trace << "no existing file for prerequisite " << cpk;});
+ return nullptr;
+ }
- if (mt == timestamp_nonexistent)
- continue;
+ l5 ([&]{trace << "found existing file " << f << " for prerequisite "
+ << cpk;});
- l5 ([&]{trace << "found existing file " << f << " for prerequisite "
- << cpk;});
+ dir_path d (f.directory ());
- // Find or insert. Note: using our updated extension.
- //
- auto r (targets.insert (*tk.type, f.directory (), *tk.name, ext, trace));
+ // Calculate the corresponding out. We have the same three options for the
+ // prerequisite's out directory as in search_existing_target(). If it is
+ // empty (undetermined), then we need to calculate it since this target
+ // will be from the src tree.
+ //
+ // In the other two cases we use the prerequisite's out (in case it is
+ // relative, we need to complete it, which is @@ OUT TODO). Note that we
+ // blindly trust the user's value which can be use for some interesting
+ // tricks, for example:
+ //
+ // ../cxx{foo}@./
+ //
+ dir_path out;
- // Has to be a file_target.
- //
- file& t (dynamic_cast<file&> (r.first));
+ if (tk.out->empty ())
+ {
+ if (pk.scope->out_path () != s)
+ out = out_src (d, *pk.scope);
+ }
+ else
+ out = *tk.out;
- l5 ([&]{trace << (r.second ? "new" : "existing") << " target " << t
- << " for prerequisite " << cpk;});
+ // Find or insert. Note that we are using our updated extension.
+ //
+ auto r (targets.insert (
+ *tk.type, move (d), move (out), *tk.name, ext, trace));
- if (t.path ().empty ())
- t.path (move (f));
+ // Has to be a file_target.
+ //
+ file& t (dynamic_cast<file&> (r.first));
- t.mtime (mt);
- return &t;
- }
+ l5 ([&]{trace << (r.second ? "new" : "existing") << " target " << t
+ << " for prerequisite " << cpk;});
+
+ if (t.path ().empty ())
+ t.path (move (f));
- l4 ([&]{trace << "no existing file for prerequisite " << cpk;});
- return nullptr;
+ t.mtime (mt);
+ return &t;
}
target&
@@ -156,7 +195,10 @@ namespace build2
// Find or insert.
//
- auto r (targets.insert (*tk.type, move (d), *tk.name, tk.ext, trace));
+ // @@ OUT: same story as in search_existing_target() re out.
+ //
+ auto r (targets.insert (
+ *tk.type, move (d), *tk.out, *tk.name, tk.ext, trace));
assert (r.second);
target& t (r.first);
diff --git a/build2/target b/build2/target
index 241f6ff..9061e2d 100644
--- a/build2/target
+++ b/build2/target
@@ -127,24 +127,24 @@ namespace build2
public:
typedef build2::action action_type;
- virtual
- ~target () = default;
-
- target (const target&) = delete;
- target& operator= (const target&) = delete;
-
- target (dir_path d, string n, const string* e)
- : dir (move (d)), name (move (n)), ext (e) {}
-
- // Reset the target before matching a rule for it. The
- // default implementation clears prerequisite_targets.
+ // For targets that are in the src tree of a project we also keep the
+ // corresponding out directory. As a result we may end up with multiple
+ // targets for the same file if we are building multiple configurations of
+ // the same project at once. We do it this way because, in a sense, a
+ // target's out directory is its "configuration" (in terms of variables).
+ // As an example, consider installing the same README file (src) but for
+ // two different project configurations at once. Which installation
+ // directory should we use? The answer depends on which configuration you
+ // ask.
+ //
+ // Empty out directory indicates this target is in the out tree (including
+ // when src == out). We also treat out of project targets as being in the
+ // out tree.
//
- virtual void
- reset (action_type);
-
const dir_path dir; // Absolute and normalized.
+ const dir_path out; // Empty or absolute and normalized.
const string name;
- const string* ext; // Extension, NULL - unspecified, empty - no extension.
+ const string* ext; // Extension. NULL - unspecified, empty - no extension.
// Target group to which this target belongs, if any. Note that
// we assume that the group and all its members are in the same
@@ -187,6 +187,22 @@ namespace build2
//
target* group = nullptr;
+ public:
+ virtual
+ ~target () = default;
+
+ target (const target&) = delete;
+ target& operator= (const target&) = delete;
+
+ target (dir_path d, dir_path o, string n, const string* e)
+ : dir (move (d)), out (move (o)), name (move (n)), ext (e) {}
+
+ // Reset the target before matching a rule for it. The default
+ // implementation clears prerequisite_targets.
+ //
+ virtual void
+ reset (action_type);
+
// You should not call this function directly; rather use
// resolve_group_members() from <build2/algorithm>.
//
@@ -197,7 +213,7 @@ namespace build2
// to the targets's members will be reflected in the key.
//
target_key
- key () const {return target_key {&type (), &dir, &name, ext};}
+ key () const {return target_key {&type (), &dir, &out, &name, ext};}
// Scoping.
//
@@ -227,13 +243,10 @@ namespace build2
scope&
weak_scope () const {return *root_scope ().weak_scope ();}
-
bool
in (const scope& s) const
{
- return
- (s.out_path_ != nullptr && dir.sub (*s.out_path_)) ||
- (s.src_path_ != nullptr && dir.sub (*s.src_path_));
+ return (out.empty () ? dir : out).sub (s.out_path ());
}
// Prerequisites.
@@ -401,8 +414,9 @@ namespace build2
recipe_type recipe_;
};
- ostream&
- operator<< (ostream&, const target&);
+ inline ostream&
+ operator<< (ostream& os, const target& t) {return os << t.key ();}
+
// A "range" that presents the prerequisites of a group and one of
// its members as one continuous sequence, or, in other words, as
@@ -780,21 +794,23 @@ namespace build2
iterator
find (const target_type& type,
const dir_path& dir,
+ const dir_path& out,
const string& name,
const string* ext,
tracer& trace) const
{
- return find (target_key {&type, &dir, &name, ext}, trace);
+ return find (target_key {&type, &dir, &out, &name, ext}, trace);
}
- // As above but ignore the extension and return the target or
- // nullptr instead of the iterator.
+ // As above but ignore the extension and return the target or nullptr
+ // instead of the iterator.
//
template <typename T>
T*
- find (const dir_path& dir, const string& name) const
+ find (const dir_path& dir, const dir_path& out, const string& name) const
{
- auto i (map_.find (target_key {&T::static_type, &dir, &name, nullptr}));
+ auto i (map_.find (
+ target_key {&T::static_type, &dir, &out, &name, nullptr}));
return i != map_.end () ? static_cast<T*> (i->second.get ()) : nullptr;
}
@@ -804,27 +820,33 @@ namespace build2
pair<target&, bool>
insert (const target_type&,
dir_path dir,
+ dir_path out,
string name,
const string* ext,
tracer&);
+
template <typename T>
T&
insert (const dir_path& dir,
+ const dir_path& out,
const string& name,
const string* ext,
tracer& t)
{
return static_cast<T&> (
- insert (T::static_type, dir, name, ext, t).first);
+ insert (T::static_type, dir, out, name, ext, t).first);
}
template <typename T>
T&
- insert (const dir_path& dir, const string& name, tracer& t)
+ insert (const dir_path& dir,
+ const dir_path& out,
+ const string& name,
+ tracer& t)
{
return static_cast<T&> (
- insert (T::static_type, dir, name, nullptr, t).first);
+ insert (T::static_type, dir, out, name, nullptr, t).first);
}
void
@@ -1072,9 +1094,13 @@ namespace build2
//
template <typename T>
target*
- target_factory (const target_type&, dir_path d, string n, const string* e)
+ target_factory (const target_type&,
+ dir_path d,
+ dir_path o,
+ string n,
+ const string* e)
{
- return new T (move (d), move (n), e);
+ return new T (move (d), move (o), move (n), e);
}
// Return fixed target extension.
@@ -1095,11 +1121,6 @@ namespace build2
const string*
target_extension_null (const target_key&, scope&);
- // Issue diagnostics and fail if called.
- //
- const string*
- target_extension_fail (const target_key&, scope&);
-
// Assert if called.
//
const string*
diff --git a/build2/target-key b/build2/target-key
index bf88007..2c0d0f2 100644
--- a/build2/target-key
+++ b/build2/target-key
@@ -22,7 +22,8 @@ namespace build2
{
public:
const target_type* const type;
- const dir_path* const dir;
+ const dir_path* const dir; // Can be relative if part of prerequisite_key.
+ const dir_path* const out; // Can be relative if part of prerequisite_key.
const string* const name;
const string* const& ext;
@@ -32,17 +33,18 @@ namespace build2
const target_type* xt (x.type);
const target_type* yt (y.type);
- //@@ TODO: use compare() to compare once.
+ int t, n, d, o;
// 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 &&
+ ((t = xt < yt ? -1 : xt > yt ? 1 : 0) < 0) ||
+ (t == 0 && (n = x.name->compare (*y.name)) < 0) ||
+ (t == 0 && n == 0 && (d = x.dir->compare (*y.dir)) < 0) ||
+ (t == 0 && n == 0 && d == 0 && (o = x.out->compare (*y.out)) < 0) ||
+ (t == 0 && n == 0 && d == 0 && o == 0 &&
x.ext != nullptr && y.ext != nullptr && *x.ext < *y.ext);
}
};
diff --git a/build2/target-type b/build2/target-type
index de210e1..6006b2c 100644
--- a/build2/target-type
+++ b/build2/target-type
@@ -31,14 +31,16 @@ namespace build2
// respectively. If the extension function returns NULL, then that means the
// default extension for this target could not be derived.
//
- // The extension function is used in two places: search_existing_file() and
- // in target::derive_path(); see their implementations for details.
+ // The extension function is used in two places: search_existing_file()
+ // (called for a prerequisite) and in target::derive_path() (called for a
+ // target); see their implementations for details.
//
struct target_type
{
const char* name;
const target_type* base;
- target* (*factory) (const target_type&, dir_path, string, const string*);
+ target* (*factory) (
+ const target_type&, dir_path, dir_path, string, const string*);
const string* (*extension) (const target_key&, scope&);
void (*print) (ostream&, const target_key&);
target* (*search) (const prerequisite_key&);
diff --git a/build2/target.cxx b/build2/target.cxx
index 782a7dd..663d31d 100644
--- a/build2/target.cxx
+++ b/build2/target.cxx
@@ -101,7 +101,10 @@ namespace build2
scope& target::
base_scope () const
{
- return scopes.find (dir);
+ // If this target is from the src tree, use its out directory to find
+ // the scope.
+ //
+ return scopes.find (out.empty () ? dir : out);
}
scope& target::
@@ -109,7 +112,7 @@ namespace build2
{
// This is tricky to cache so we do the lookup for now.
//
- scope* r (scopes.find (dir).root_scope ());
+ scope* r (base_scope ().root_scope ());
assert (r != nullptr);
return *r;
}
@@ -170,12 +173,6 @@ namespace build2
return r;
}
- ostream&
- operator<< (ostream& os, const target& t)
- {
- return os << target_key {&t.type (), &t.dir, &t.name, t.ext};
- }
-
// target_set
//
target_set targets;
@@ -216,18 +213,20 @@ namespace build2
pair<target&, bool> target_set::
insert (const target_type& tt,
dir_path dir,
+ dir_path out,
string name,
const string* ext,
tracer& trace)
{
- iterator i (find (target_key {&tt, &dir, &name, ext}, trace));
+ iterator i (find (target_key {&tt, &dir, &out, &name, ext}, trace));
bool r (i == end ());
if (r)
{
- unique_ptr<target> pt (tt.factory (tt, move (dir), move (name), ext));
+ unique_ptr<target> pt (
+ tt.factory (tt, move (dir), move (out), move (name), ext));
i = map_.emplace (
- make_pair (target_key {&tt, &pt->dir, &pt->name, pt->ext},
+ make_pair (target_key {&tt, &pt->dir, &pt->out, &pt->name, pt->ext},
move (pt))).first;
}
@@ -280,6 +279,11 @@ namespace build2
os << '}';
+ // If this target is from src, print its out.
+ //
+ if (!k.out->empty ())
+ os << '@' << diag_relative (*k.out, false); // Don't print './'.
+
return os;
}
@@ -382,17 +386,9 @@ namespace build2
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).
+ // Then look for an existing file in the src tree.
//
- if (pk.tk.dir->relative ())
- {
- dir_paths sp;
- sp.push_back (pk.scope->src_path ()); // src_base
- return search_existing_file (pk, sp);
- }
- else
- return nullptr;
+ return pk.tk.dir->relative () ? search_existing_file (pk) : nullptr;
}
static target*
@@ -416,27 +412,6 @@ namespace build2
}
const string*
- target_extension_fail (const target_key& tk, scope& s)
- {
- {
- diag_record dr;
- dr << error << "no default extension to derive file name for ";
-
- // This is a bit hacky: we may be dealing with a target (see
- // file::derive_path()) or prerequisite (see search_existing_file()). So
- // we are going to check if dir is absolute. If it is, then we assume
- // this is a target, otherwise -- prerequisite.
- //
- if (tk.dir->absolute ())
- dr << "target " << tk;
- else
- dr << "prerequisite " << prerequisite_key {nullptr, tk, &s};
- }
-
- throw failed ();
- }
-
- const string*
target_extension_assert (const target_key&, scope&)
{
assert (false); // Attempt to obtain the default extension.
@@ -495,13 +470,18 @@ namespace build2
template <typename T>
static target*
- file_factory (const target_type&, dir_path d, string n, const string* e)
- {
- // The file target type doesn't imply any extension. So if one
- // wasn't specified, set it to empty rather than unspecified.
- // In other words, we always treat file{foo} as file{foo.}.
+ file_factory (const target_type&,
+ dir_path d,
+ dir_path o,
+ string n,
+ const string* e)
+ {
+ // The file target type doesn't imply any extension. So if one wasn't
+ // specified, set it to empty rather than unspecified. In other words, we
+ // always treat file{foo} as file{foo.}.
//
return new T (move (d),
+ move (o),
move (n),
(e != nullptr ? e : &extension_pool.find ("")));
}
@@ -585,12 +565,16 @@ namespace build2
};
static target*
- man_factory (const target_type&, dir_path d, string n, const string* e)
+ man_factory (const target_type&,
+ dir_path d,
+ dir_path o,
+ string n,
+ const string* e)
{
if (e == nullptr)
fail << "man target '" << n << "' must include extension (man section)";
- return new man (move (d), move (n), e);
+ return new man (move (d), move (o), move (n), e);
}
const target_type man::static_type
diff --git a/build2/test/rule.cxx b/build2/test/rule.cxx
index ccf52e0..8a4c15d 100644
--- a/build2/test/rule.cxx
+++ b/build2/test/rule.cxx
@@ -139,6 +139,8 @@ namespace build2
//
scope& bs (t.base_scope ());
+ // @@ OUT: what if this is a @-qualified pair or names?
+ //
target* it (in != nullptr ? &search (*in, bs) : nullptr);
target* ot (on != nullptr ? in == on ? it : &search (*on, bs) : nullptr);