From fc18a6dc1fcb02b505f07914e484cebbaf268698 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 19 Apr 2016 09:24:38 +0200 Subject: Redesign src/out scoping We no longer enter paths from the src tree into scope map. Instead, targets from the src tree now include their out tree directory (in essence their "configuration"). This is then used to find a target's scope. See the comment in class target for details. The result of this change is that we can now again build multiple configurations (out trees) for same project at once. --- build2/algorithm | 2 + build2/algorithm.cxx | 9 ++- build2/algorithm.ixx | 11 +-- build2/b.cxx | 8 ++- build2/bin/rule.cxx | 4 +- build2/bin/target.cxx | 64 ++++++++++++------ build2/cli/rule.cxx | 10 +-- build2/cli/target.cxx | 14 ++-- build2/config/operation.cxx | 8 ++- build2/context | 6 +- build2/context.cxx | 29 ++++---- build2/cxx/compile.cxx | 20 ++++-- build2/cxx/link.cxx | 28 +++++--- build2/dist/operation.cxx | 41 ++++++----- build2/dump.cxx | 35 ++++------ build2/file.cxx | 69 +++++++++---------- build2/operation.cxx | 7 +- build2/parser.cxx | 73 ++++++++++++++------ build2/prerequisite | 37 +++++----- build2/prerequisite.cxx | 5 +- build2/scope | 62 +++++------------ build2/scope.cxx | 161 +++++++++++++++----------------------------- build2/search | 8 ++- build2/search.cxx | 110 ++++++++++++++++++++---------- build2/target | 95 ++++++++++++++++---------- build2/target-key | 14 ++-- build2/target-type | 8 ++- build2/target.cxx | 82 +++++++++------------- build2/test/rule.cxx | 2 + 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 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 @@ -323,7 +326,9 @@ namespace build2 l6 ([&]{trace << "for " << t;}); - fsdir* r (&search (d, string (), nullptr, &s)); + // Target in the out tree, so out directory is empty. + // + fsdir* r (&search (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 inline T& search (const dir_path& dir, + const dir_path& out, const string& name, const string* ext, scope* scope) { - return static_cast (search (T::static_type, dir, name, ext, scope)); + return static_cast ( + search (T::static_type, dir, out, name, ext, scope)); } pair 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 (t.dir, t.name, t.ext, nullptr); + t.a = &search (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 (t.dir, t.name, t.ext, nullptr); + t.so = &search (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 (d, n)); - obja* a (new obja (move (d), move (n), e)); + obj* o (targets.find (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 (d, n)); - objso* so (new objso (move (d), move (n), e)); + obj* o (targets.find (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 (d, n)); - objso* so (targets.find (d, n)); - obj* o (new obj (move (d), move (n), e)); + obja* a (targets.find (dir, out, n)); + objso* so (targets.find (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 (d, n) : nullptr); - liba* a (new liba (move (d), move (n), e)); + lib* l (t == liba::static_type ? targets.find (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 (d, n) : nullptr); - libso* so (new libso (move (d), move (n), e)); + lib* l (t == libso::static_type ? targets.find (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 (d, n)); - libso* so (targets.find (d, n)); - lib* l (new lib (move (d), move (n), e)); + liba* a (targets.find (d, o, n)); + libso* so (targets.find (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 (t.dir, t.name, nullptr, nullptr); + t.h = &search (t.dir, t.out, t.name, nullptr, nullptr); t.h->group = &t; - t.c = &search (t.dir, t.name, nullptr, nullptr); + t.c = &search (t.dir, t.out, t.name, nullptr, nullptr); t.c->group = &t; if (!find_option ("--suppress-inline", t, "cli.options")) { - t.i = &search (t.dir, t.name, nullptr, nullptr); + t.i = &search (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 (t.dir, t.name)); + cli_cxx* g (targets.find (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 (t.dir, t.name, trace); + g = &targets.insert (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 (d, n, trace); - targets.insert (d, n, trace); - targets.insert (d, n, trace); + targets.insert (d, o, n, trace); + targets.insert (d, o, n, trace); + targets.insert (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 // 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 (search (*tt, d, n, e, &ds))); + static_cast (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 (d, p.name, ae, trace); + // Note that this target is outside any project which we treat + // as out trees. + // + a = &targets.insert (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 (d, p.name, se, trace); + s = &targets.insert (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 (*pd, p.name, p.ext, trace)); + lib& l (targets.insert (*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 ( - 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 ( + 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 (v)); - // Register and set src_path. - // if (s.src_path_ == nullptr) - s.src_path_ = &scopes.insert ( - cast (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 (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 (v) == out_base); - } + if (!sv) + sv = src_base; + else + assert (cast (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 (v) == src_base); - } + if (s.src_path_ == nullptr) + s.src_path_ = &cast (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 ( - 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 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 (*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; + + class scope_map: public scope_map_base { public: - using map_type = butl::dir_path_map; - 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 (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 (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 . // @@ -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 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 (i->second.get ()) : nullptr; } @@ -804,27 +820,33 @@ namespace build2 pair insert (const target_type&, dir_path dir, + dir_path out, string name, const string* ext, tracer&); + template T& insert (const dir_path& dir, + const dir_path& out, const string& name, const string* ext, tracer& t) { return static_cast ( - insert (T::static_type, dir, name, ext, t).first); + insert (T::static_type, dir, out, name, ext, t).first); } template 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 ( - 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 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_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 pt (tt.factory (tt, move (dir), move (name), ext)); + unique_ptr 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 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); -- cgit v1.1