diff options
Diffstat (limited to 'libbuild2/search.cxx')
-rw-r--r-- | libbuild2/search.cxx | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/libbuild2/search.cxx b/libbuild2/search.cxx new file mode 100644 index 0000000..1ff9c73 --- /dev/null +++ b/libbuild2/search.cxx @@ -0,0 +1,244 @@ +// file : libbuild2/search.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <libbuild2/search.hxx> + +#include <libbuild2/scope.hxx> +#include <libbuild2/target.hxx> +#include <libbuild2/context.hxx> +#include <libbuild2/filesystem.hxx> // mtime() +#include <libbuild2/prerequisite.hxx> +#include <libbuild2/diagnostics.hxx> + +using namespace std; +using namespace butl; + +namespace build2 +{ + const target* + search_existing_target (const prerequisite_key& pk) + { + tracer trace ("search_existing_target"); + + const target_key& tk (pk.tk); + + // Look for an existing target in the prerequisite's scope. + // + dir_path d; + if (tk.dir->absolute ()) + d = *tk.dir; // Already normalized. + else + { + d = tk.out->empty () ? pk.scope->out_path () : pk.scope->src_path (); + + if (!tk.dir->empty ()) + { + d /= *tk.dir; + d.normalize (); + } + } + + // 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. + // + // 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. + // + dir_path o; + if (!tk.out->empty ()) + { + if (tk.out->absolute ()) + o = *tk.out; // Already normalized. + else + { + o = pk.scope->out_path (); + o /= *tk.out; + o.normalize (); + } + + // Drop out if it is the same as src (in-src build). + // + if (o == d) + o.clear (); + } + + const target* t (targets.find (*tk.type, d, o, *tk.name, tk.ext, trace)); + + if (t != nullptr) + l5 ([&]{trace << "existing target " << *t + << " for prerequisite " << pk;}); + + return t; + } + + const target* + search_existing_file (const prerequisite_key& cpk) + { + tracer trace ("search_existing_file"); + + const target_key& ctk (cpk.tk); + const scope* s (cpk.scope); + + path f; + + if (ctk.dir->absolute ()) + f = *ctk.dir; // Already normalized. + else + { + f = s->src_path (); + + if (!ctk.dir->empty ()) + { + f /= *ctk.dir; + f.normalize (); + } + } + + // Bail out if not inside project's src_root. + // + if (s == nullptr || !f.sub (s->root_scope ()->src_path ())) + return nullptr; + + // Figure out the extension. Pretty similar logic to file::derive_path(). + // + optional<string> ext (ctk.ext); + + if (!ext) + { + if (auto f = ctk.type->fixed_extension) + ext = f (ctk, s->root_scope ()); + else if (auto f = ctk.type->default_extension) + ext = f (ctk, *s, nullptr, true); + + if (!ext) + { + // What should we do here, fail or say we didn't find anything? + // Current think is that if the target type couldn't find the default + // extension, then we simply shouldn't search for any existing files + // (of course, if the user specified the extension explicitly, we will + // still do so). + // + l4 ([&]{trace << "no default extension for prerequisite " << cpk;}); + return nullptr; + } + } + + // Make a copy with the updated extension. + // + const prerequisite_key pk { + cpk.proj, {ctk.type, ctk.dir, ctk.out, ctk.name, ext}, cpk.scope}; + const target_key& tk (pk.tk); + + // Check if there is a file. + // + f /= *tk.name; + + if (!ext->empty ()) + { + f += '.'; + f += *ext; + } + + timestamp mt (mtime (f)); + + if (mt == timestamp_nonexistent) + { + l4 ([&]{trace << "no existing file for prerequisite " << cpk;}); + return nullptr; + } + + l5 ([&]{trace << "found existing file " << f << " for prerequisite " + << cpk;}); + + dir_path d (f.directory ()); + + // 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 used for some interesting + // tricks, for example: + // + // ../cxx{foo}@./ + // + dir_path out; + + if (tk.out->empty ()) + { + if (s->out_path () != s->src_path ()) + out = out_src (d, *s->root_scope ()); + } + else + out = *tk.out; + + // Find or insert. Note that we are using our updated extension. + // + auto r ( + targets.insert ( + *tk.type, move (d), move (out), *tk.name, ext, true, trace)); + + // Has to be a file_target. + // + const file& t (dynamic_cast<const file&> (r.first)); + + l5 ([&]{trace << (r.second ? "new" : "existing") << " target " << t + << " for prerequisite " << cpk;}); + + t.mtime (mt); + t.path (move (f)); + + return &t; + } + + const target& + create_new_target (const prerequisite_key& pk) + { + tracer trace ("create_new_target"); + + const target_key& tk (pk.tk); + + // We default to the target in this directory scope. + // + dir_path d; + if (tk.dir->absolute ()) + d = *tk.dir; // Already normalized. + else + { + d = pk.scope->out_path (); + + if (!tk.dir->empty ()) + { + d /= *tk.dir; + d.normalize (); + } + } + + // Find or insert. + // + // @@ OUT: same story as in search_existing_target() re out. + // + auto r (targets.insert (*tk.type, + move (d), + *tk.out, + *tk.name, + tk.ext, + true /* implied */, + trace)); + + const target& t (r.first); + l5 ([&]{trace << (r.second ? "new" : "existing") << " target " << t + << " for prerequisite " << pk;}); + return t; + } +} |