diff options
-rw-r--r-- | build2/algorithm | 2 | ||||
-rw-r--r-- | build2/algorithm.cxx | 9 | ||||
-rw-r--r-- | build2/algorithm.ixx | 11 | ||||
-rw-r--r-- | build2/b.cxx | 8 | ||||
-rw-r--r-- | build2/bin/rule.cxx | 4 | ||||
-rw-r--r-- | build2/bin/target.cxx | 64 | ||||
-rw-r--r-- | build2/cli/rule.cxx | 10 | ||||
-rw-r--r-- | build2/cli/target.cxx | 14 | ||||
-rw-r--r-- | build2/config/operation.cxx | 8 | ||||
-rw-r--r-- | build2/context | 6 | ||||
-rw-r--r-- | build2/context.cxx | 29 | ||||
-rw-r--r-- | build2/cxx/compile.cxx | 20 | ||||
-rw-r--r-- | build2/cxx/link.cxx | 28 | ||||
-rw-r--r-- | build2/dist/operation.cxx | 41 | ||||
-rw-r--r-- | build2/dump.cxx | 35 | ||||
-rw-r--r-- | build2/file.cxx | 69 | ||||
-rw-r--r-- | build2/operation.cxx | 7 | ||||
-rw-r--r-- | build2/parser.cxx | 73 | ||||
-rw-r--r-- | build2/prerequisite | 37 | ||||
-rw-r--r-- | build2/prerequisite.cxx | 5 | ||||
-rw-r--r-- | build2/scope | 62 | ||||
-rw-r--r-- | build2/scope.cxx | 161 | ||||
-rw-r--r-- | build2/search | 8 | ||||
-rw-r--r-- | build2/search.cxx | 110 | ||||
-rw-r--r-- | build2/target | 95 | ||||
-rw-r--r-- | build2/target-key | 14 | ||||
-rw-r--r-- | build2/target-type | 8 | ||||
-rw-r--r-- | build2/target.cxx | 82 | ||||
-rw-r--r-- | 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 <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); |