diff options
Diffstat (limited to 'libbuild2/search.cxx')
-rw-r--r-- | libbuild2/search.cxx | 171 |
1 files changed, 152 insertions, 19 deletions
diff --git a/libbuild2/search.cxx b/libbuild2/search.cxx index 2b10e0b..4e855e3 100644 --- a/libbuild2/search.cxx +++ b/libbuild2/search.cxx @@ -15,7 +15,9 @@ using namespace butl; namespace build2 { const target* - search_existing_target (context& ctx, const prerequisite_key& pk) + search_existing_target (context& ctx, + const prerequisite_key& pk, + bool out_only) { tracer trace ("search_existing_target"); @@ -39,9 +41,10 @@ namespace build2 // Prerequisite's out directory can be one 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. + // empty This means out is undetermined and we search for a target + // first in the out tree (which happens to be indicated by an + // empty value, so we can just pass this as is) and if not + // found, then in the src tree (unless suppressed). // // absolute This is the "final" value that doesn't require any processing // and we simply use it as is. @@ -58,8 +61,11 @@ namespace build2 else { o = pk.scope->out_path (); - o /= *tk.out; - o.normalize (); + if (!tk.out->current ()) + { + o /= *tk.out; + o.normalize (); + } } // Drop out if it is the same as src (in-src build). @@ -71,6 +77,27 @@ namespace build2 const target* t ( ctx.targets.find (*tk.type, d, o, *tk.name, tk.ext, trace)); + // Try in the src tree. + // + if (t == nullptr && + !out_only && + tk.out->empty () && + tk.dir->relative () && + !pk.scope->out_eq_src ()) + { + o = move (d); + + d = pk.scope->src_path (); + + if (!tk.dir->empty ()) + { + d /= *tk.dir; + d.normalize (); + } + + t = ctx.targets.find (*tk.type, d, o, *tk.name, tk.ext, trace); + } + if (t != nullptr) l5 ([&]{trace << "existing target " << *t << " for prerequisite " << pk;}); @@ -86,6 +113,10 @@ namespace build2 const target_key& ctk (cpk.tk); const scope* s (cpk.scope); + // Has to be a file target. + // + assert (ctk.type->is_a<file> ()); + path f; if (ctk.dir->absolute ()) @@ -165,35 +196,49 @@ namespace build2 // 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 used for some interesting - // tricks, for example: - // - // ../cxx{foo}@./ + // relative, we need to complete it). // dir_path out; if (tk.out->empty ()) { - if (s->out_path () != s->src_path ()) + if (!s->out_eq_src ()) out = out_src (d, *s->root_scope ()); } else - out = *tk.out; + { + if (tk.out->absolute ()) + out = *tk.out; // Already normalized. + else + { + out = pk.scope->out_path (); + if (!tk.out->current ()) + { + out /= *tk.out; + out.normalize (); + } + } + + // Drop out if it is the same as src (in-src build). + // + if (out == d) + out.clear (); + } // Find or insert. Note that we are using our updated extension. // + // More often insert than find, so skip find in insert(). + // auto r (ctx.targets.insert (*tk.type, move (d), move (out), *tk.name, ext, target_decl::prereq_file, - trace)); + trace, + true /* skip_find */)); - // Has to be a file_target. - // - const file& t (dynamic_cast<const file&> (r.first)); + const file& t (r.first.as<file> ()); l5 ([&]{trace << (r.second ? "new" : "existing") << " target " << t << " for prerequisite " << cpk;}); @@ -210,11 +255,34 @@ namespace build2 const target_key& tk (pk.tk); + // If out is present, then it means the target is in src and we shouldn't + // be creating new targets in src, should we? Feels like this should not + // even be called if out is not empty. + // + assert (tk.out->empty ()); + // We default to the target in this directory scope. // dir_path d; if (tk.dir->absolute ()) + { d = *tk.dir; // Already normalized. + + // Even if out is empty, it may still be (only) in src. + // + // Note: issue diagnostics consistent with search() after skipping this + // function due to non-empty out. + // + // @@ PERF: we could first check if it's in pk.scope, which feels like + // the common case. Though this doesn't seem to affect + // performance in any noticeable way. + // + auto p (ctx.scopes.find (d, false)); // Note: never empty. + if (*p.first == nullptr && ++p.first != p.second) + { + fail << "no existing source file for prerequisite " << pk << endf; + } + } else { d = pk.scope->out_path (); @@ -228,7 +296,7 @@ namespace build2 // Find or insert. // - // @@ OUT: same story as in search_existing_target() re out. + // More often insert than find, so skip find in insert(). // auto r (ctx.targets.insert (*tk.type, move (d), @@ -236,11 +304,76 @@ namespace build2 *tk.name, tk.ext, target_decl::prereq_new, - trace)); + trace, + true /* skip_find */)); const target& t (r.first); l5 ([&]{trace << (r.second ? "new" : "existing") << " target " << t << " for prerequisite " << pk;}); return t; } + + pair<target&, ulock> + create_new_target_locked (context& ctx, const prerequisite_key& pk) + { + tracer trace ("create_new_target_locked"); + + const target_key& tk (pk.tk); + + // If out is present, then it means the target is in src and we shouldn't + // be creating new targets in src, should we? Feels like this should not + // even be called if out is not empty. + // + assert (tk.out->empty ()); + + // We default to the target in this directory scope. + // + dir_path d; + if (tk.dir->absolute ()) + { + d = *tk.dir; // Already normalized. + + // As above. + // + auto p (ctx.scopes.find (d, false)); + if (*p.first == nullptr && ++p.first != p.second) + { + fail << "no existing source file for prerequisite " << pk << endf; + } + } + else + { + d = pk.scope->out_path (); + + if (!tk.dir->empty ()) + { + d /= *tk.dir; + d.normalize (); + } + } + + // Find or insert. + // + // More often insert than find, so skip find in insert_locked(). + // + auto r (ctx.targets.insert_locked (*tk.type, + move (d), + *tk.out, + *tk.name, + tk.ext, + target_decl::prereq_new, + trace, + true /* skip_find */)); + l5 ([&] + { + diag_record dr (trace); + if (r.second) + dr << "new target " << r.first.key_locked (); + else + dr << "existing target " << r.first; + dr << " for prerequisite " << pk; + }); + + return r; + } } |