From 9fb791e9fad6c63fc1dac49f4d05ae63b8a3db9b Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 5 Jan 2016 11:55:15 +0200 Subject: Rename build directory/namespace to build2 --- build2/target.cxx | 537 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 537 insertions(+) create mode 100644 build2/target.cxx (limited to 'build2/target.cxx') diff --git a/build2/target.cxx b/build2/target.cxx new file mode 100644 index 0000000..3932466 --- /dev/null +++ b/build2/target.cxx @@ -0,0 +1,537 @@ +// file : build2/target.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include + +#include + +#include +#include +#include +#include + +using namespace std; + +namespace build2 +{ + // target_type + // + bool target_type:: + is_a (const target_type& tt) const + { + for (const target_type* p (this); p != nullptr; p = p->base) + if (*p == tt) + return true; + + return false; + } + + // target_state + // + static const char* target_state_[] = { + "unknown", "unchanged", "postponed", "changed", "failed", "group"}; + + ostream& + operator<< (ostream& os, target_state ts) + { + return os << target_state_[static_cast (ts)]; + } + + // recipe + // + const recipe empty_recipe; + const recipe noop_recipe (&noop_action); + const recipe default_recipe (&default_action); + const recipe group_recipe (&group_action); + + // target + // + + void target:: + recipe (action_type a, recipe_type r) + { + assert (a > action || !recipe_); + + bool override (a == action && recipe_); // See action::operator<. + + // Only noop_recipe can be overridden. + // + if (override) + { + recipe_function** f (recipe_.target ()); + assert (f != nullptr && *f == &noop_action); + } + + action = a; + recipe_ = std::move (r); + + // Also reset the target state. If this is a noop recipe, then + // mark the target unchanged so that we don't waste time executing + // the recipe. + // + raw_state = target_state::unknown; + + if (recipe_function** f = recipe_.target ()) + { + if (*f == &noop_action) + raw_state = target_state::unchanged; + } + + // This one is tricky: we don't want to reset the dependents count + // if we are merely overriding with a "stronger" recipe. + // + if (!override) + dependents = 0; + } + + void target:: + reset (action_type) + { + prerequisite_targets.clear (); + } + + group_view target:: + group_members (action_type) const + { + assert (false); // Not a group or doesn't expose its members. + return group_view {nullptr, 0}; + } + + scope& target:: + base_scope () const + { + return scopes.find (dir); + } + + scope& target:: + root_scope () const + { + // This is tricky to cache so we do the lookup for now. + // + scope* r (scopes.find (dir).root_scope ()); + assert (r != nullptr); + return *r; + } + + lookup target:: + operator[] (const variable& var) const + { + using result = lookup; + + if (auto p = vars.find (var)) + return result (p, &vars); + + if (group != nullptr) + { + if (auto p = group->vars.find (var)) + return result (p, &group->vars); + } + + // We cannot simply delegate to scope's lookup() since we also need + // to check the group. + // + for (const scope* s (&base_scope ()); s != nullptr; ) + { + if (!s->target_vars.empty ()) + { + if (auto l = s->target_vars.lookup (type (), name, var)) + return l; + + if (group != nullptr) + { + if (auto l = s->target_vars.lookup (group->type (), group->name, var)) + return l; + } + } + + if (auto r = s->vars.find (var)) + return result (r, &s->vars); + + switch (var.visibility) + { + case variable_visibility::scope: + s = nullptr; + break; + case variable_visibility::project: + s = s->root () ? nullptr : s->parent_scope (); + break; + case variable_visibility::normal: + s = s->parent_scope (); + break; + } + } + + return result (); + } + + value& target:: + append (const variable& var) + { + auto l (operator[] (var)); + + if (l && l.belongs (*this)) // Existing variable in this target. + return const_cast (*l); + + value& r (assign (var)); + + if (l) + r = *l; // Copy value from the outer scope. + + 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; + + auto target_set:: + find (const target_key& k, tracer& trace) const -> iterator + { + iterator i (map_.find (k)); + + if (i != end ()) + { + target& t (**i); + + // Update the extension if the existing target has it unspecified. + // + const string* ext (*k.ext); + if (t.ext != ext) + { + level5 ([&]{ + diag_record r (trace); + r << "assuming target " << t << " is the same as the one with "; + if (ext == nullptr) + r << "unspecified extension"; + else if (ext->empty ()) + r << "no extension"; + else + r << "extension " << *ext; + }); + + if (ext != nullptr) + t.ext = ext; + } + } + + return i; + } + + pair target_set:: + insert (const target_type& tt, + dir_path dir, + string name, + const string* ext, + tracer& trace) + { + iterator i (find (target_key {&tt, &dir, &name, &ext}, trace)); + bool r (i == end ()); + + if (r) + { + unique_ptr pt (tt.factory (tt, move (dir), move (name), ext)); + i = map_.emplace ( + make_pair (target_key {&tt, &pt->dir, &pt->name, &pt->ext}, + move (pt))).first; + } + + return pair (**i, r); + } + + ostream& + operator<< (ostream& os, const target_key& k) + { + // If the name is empty, then we want to print the directory + // inside {}, e.g., dir{bar/}, not bar/dir{}. + // + bool n (!k.name->empty ()); + string d (diag_relative (*k.dir, false)); + + if (n) + os << d; + + os << k.type->name << '{'; + + if (n) + { + os << *k.name; + + if (*k.ext != nullptr && !(*k.ext)->empty ()) + os << '.' << **k.ext; + } + else + os << d; + + os << '}'; + + return os; + } + + // path_target + // + void path_target:: + derive_path (const char* de, const char* np, const char* ns) + { + string n; + + if (np != nullptr) + n += np; + + n += name; + + if (ns != nullptr) + n += ns; + + // Update the extension. + // + // See also search_existing_file() if updating anything here. + // + if (ext == nullptr) + { + // If provided by the caller, then use that. + // + if (de != nullptr) + ext = &extension_pool.find (de); + // + // Otherwis see if the target type has function that will + // give us the default extension. + // + else if (auto f = type ().extension) + ext = &f (key (), base_scope ()); // Already from the pool. + else + fail << "no default extension for target " << *this; + } + + // Add the extension. + // + if (!ext->empty ()) + { + n += '.'; + n += *ext; + } + + path_type p (dir / path_type (move (n))); + const path_type& ep (path ()); + + if (ep.empty ()) + path (p); + else if (p != ep) + fail << "path mismatch for target " << *this << + info << "assigned '" << ep << "'" << + info << "derived '" << p << "'"; + } + + // file_target + // + timestamp file:: + load_mtime () const + { + const path_type& f (path ()); + assert (!f.empty ()); + return file_mtime (f); + } + + // Search functions. + // + + target* + search_target (const prerequisite_key& pk) + { + // The default behavior is to look for an existing target in the + // prerequisite's directory scope. + // + return search_existing_target (pk); + } + + target* + search_file (const prerequisite_key& pk) + { + // First see if there is an existing target. + // + 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). + // + 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; + } + + static target* + search_alias (const prerequisite_key& pk) + { + // For an alias we don't want to silently create a target since it + // will do nothing and it most likely not what the user intended. + // + target* t (search_existing_target (pk)); + + if (t == nullptr) + fail << "no explicit target for prerequisite " << pk; + + return t; + } + + // type info + // + + const target_type target::static_type + { + "target", + nullptr, + nullptr, + nullptr, + &search_target, + false + }; + + const target_type mtime_target::static_type + { + "mtime_target", + &target::static_type, + nullptr, + nullptr, + &search_target, + false + }; + + const target_type path_target::static_type + { + "path_target", + &mtime_target::static_type, + nullptr, + nullptr, + &search_target, + false + }; + + 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.}. + // + return new T (move (d), + move (n), + (e != nullptr ? e : &extension_pool.find (""))); + } + + constexpr const char file_ext_var[] = "extension"; + constexpr const char file_ext_def[] = ""; + + const target_type file::static_type + { + "file", + &path_target::static_type, + &file_factory, + &target_extension_var, + &search_file, + false + }; + + const target_type alias::static_type + { + "alias", + &target::static_type, + &target_factory, + nullptr, // Should never need. + &search_alias, + false + }; + + const target_type dir::static_type + { + "dir", + &alias::static_type, + &target_factory, + nullptr, // Should never need. + &search_alias, + false + }; + + const target_type fsdir::static_type + { + "fsdir", + &target::static_type, + &target_factory, + nullptr, // Should never need. + &search_target, + false + }; + + static const std::string& + buildfile_target_extension (const target_key& tk, scope&) + { + // If the name is special 'buildfile', then there is no extension, + // otherwise it is .build. + // + return extension_pool.find (*tk.name == "buildfile" ? "" : "build"); + } + + const target_type buildfile::static_type + { + "buildfile", + &file::static_type, + &file_factory, + &buildfile_target_extension, + &search_file, + false + }; + + const target_type doc::static_type + { + "doc", + &file::static_type, + &file_factory, + &target_extension_var, // Same as file. + &search_file, + false + }; + + static target* + man_factory (const target_type&, dir_path d, 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); + } + + const target_type man::static_type + { + "man", + &doc::static_type, + &man_factory, + nullptr, // Should be specified explicitly. + &search_file, + false + }; + + constexpr const char man1_ext[] = "1"; + const target_type man1::static_type + { + "man1", + &man::static_type, + &file_factory, + &target_extension_fix, + &search_file, + false + }; +} -- cgit v1.1