// file : libbuild2/functions-name.cxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #include #include #include #include using namespace std; namespace build2 { // Convert name to target'ish name (see below for the 'ish part). Return // raw/unprocessed data in case this is an unknown target type (or called // out of scope). See scope::find_target_type() for details. // static pair> to_target_name (const scope* s, name&& n) { optional e; if (s != nullptr) { auto rp (s->find_target_type (n, location ())); if (rp.first != nullptr) n.type = rp.first->name; e = move (rp.second); } return make_pair (move (n), move (e)); } // Note: this helper mey be used by other functions that operate on targets. // LIBBUILD2_SYMEXPORT const target& to_target (const scope& s, name&& n, name&& o) { if (const target* r = search_existing (n, s, o.dir)) return *r; fail << "target " << (n.pair ? names {move (n), move (o)} : names {move (n)}) << " not found" << endf; } void name_functions (function_map& m) { // These functions treat a name as a target/prerequisite name. // // While on one hand it feels like calling them target.name(), etc., would // have been more appropriate, on the other hand they can also be called // on prerequisite names. They also won't always return the same result as // if we were interrogating an actual target (e.g., the directory may be // relative). Plus we now have functions that can only be called on // targets (see below). // function_family fn (m, "name"); fn["name"] += [](const scope* s, name n) { return to_target_name (s, move (n)).first.value; }; fn["name"] += [](const scope* s, names ns) { return to_target_name (s, convert (move (ns))).first.value; }; // Note: returns NULL if extension is unspecified (default) and empty if // specified as no extension. // fn["extension"] += [](const scope* s, name n) { return to_target_name (s, move (n)).second; }; fn["extension"] += [](const scope* s, names ns) { return to_target_name (s, convert (move (ns))).second; }; fn["directory"] += [](const scope* s, name n) { return to_target_name (s, move (n)).first.dir; }; fn["directory"] += [](const scope* s, names ns) { return to_target_name (s, convert (move (ns))).first.dir; }; fn["target_type"] += [](const scope* s, name n) { return to_target_name (s, move (n)).first.type; }; fn["target_type"] += [](const scope* s, names ns) { return to_target_name (s, convert (move (ns))).first.type; }; // Note: returns NULL if no project specified. // fn["project"] += [](const scope* s, name n) { return to_target_name (s, move (n)).first.proj; }; fn["project"] += [](const scope* s, names ns) { return to_target_name (s, convert (move (ns))).first.proj; }; // Functions that can be called only on real targets. // function_family ft (m, "target"); // Note that while this function is not technically pure, we don't mark it // as such since it can only be called (normally form a recipe) after the // target has been matched, meaning that this target is a prerequisite and // therefore this impurity has been accounted for. // ft["path"] += [](const scope* s, names ns) { if (s == nullptr) fail << "target.path() called out of scope"; // Most of the time we will have a single target so optimize for that. // small_vector r; for (auto i (ns.begin ()); i != ns.end (); ++i) { name& n (*i), o; const target& t (to_target (*s, move (n), move (n.pair ? *++i : o))); if (const auto* pt = t.is_a ()) { const path& p (pt->path ()); if (&p != &empty_path) r.push_back (p); else fail << "target " << t << " path is not assigned"; } else fail << "target " << t << " is not path-based"; } // We want the result to be path if we were given a single target and // paths if multiple (or zero). The problem is, we cannot distinguish it // based on the argument type (e.g., name vs names) since passing an // out-qualified single target requires two names. // if (r.size () == 1) return value (move (r[0])); return value (paths (make_move_iterator (r.begin ()), make_move_iterator (r.end ()))); }; // This one can only be called on a single target since we don't support // containers of process_path's (though we probably could). // // Note that while this function is not technically pure, we don't mark it // as such for the same reasons as $path() above. // fn["process_path"] += [](const scope* s, names ns) { if (s == nullptr) fail << "target.process_path() called out of scope"; if (ns.empty () || ns.size () != (ns[0].pair ? 2 : 1)) fail << "target.process_path() expects single target"; name o; const target& t ( to_target (*s, move (ns[0]), move (ns[0].pair ? ns[1] : o))); if (const auto* et = t.is_a ()) { process_path r (et->process_path ()); if (r.empty ()) fail << "target " << t << " path is not assigned"; return r; } else fail << "target " << t << " is not process_path-based" << endf; }; // Name-specific overloads from builtins. // function_family fb (m, "builtin"); fb[".concat"] += [](dir_path d, name n) { d /= n.dir; n.dir = move (d); return n; }; } }