diff options
Diffstat (limited to 'build/rule.cxx')
-rw-r--r-- | build/rule.cxx | 249 |
1 files changed, 0 insertions, 249 deletions
diff --git a/build/rule.cxx b/build/rule.cxx deleted file mode 100644 index cce291c..0000000 --- a/build/rule.cxx +++ /dev/null @@ -1,249 +0,0 @@ -// file : build/rule.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include <build/rule> - -#include <utility> // move() -#include <system_error> - -#include <butl/filesystem> - -#include <build/scope> -#include <build/target> -#include <build/algorithm> -#include <build/diagnostics> -#include <build/context> - -using namespace std; -using namespace butl; - -namespace build -{ - // file_rule - // - // Note that this rule is special. It is the last, fallback rule. If - // it doesn't match, then no other rule can possibly match and we have - // an error. It also cannot be ambigious with any other rule. As a - // result the below implementation bends or ignores quite a few rules - // that normal implementations should follow. So you probably shouldn't - // use it as a guide to implement your own, normal, rules. - // - match_result file_rule:: - match (action a, target& t, const string&) const - { - tracer trace ("file_rule::match"); - - // While strictly speaking we should check for the file's existence - // for every action (because that's the condition for us matching), - // for some actions this is clearly a waste. Say, perform_clean: we - // are not doing anything for this action so not checking if the file - // exists seems harmless. So the overall guideline seems to be this: - // if we don't do anything for the action (other than performing it - // on the prerequisites), then we match. - // - switch (a) - { - case perform_update_id: - { - path_target& pt (dynamic_cast<path_target&> (t)); - - // Assign the path. While normally we shouldn't do this in match(), - // no other rule should ever be ambiguous with the fallback one. - // - if (pt.path ().empty ()) - pt.derive_path (); - - // We cannot just call pt.mtime() since we haven't matched yet. - // - timestamp ts (file_mtime (pt.path ())); - pt.mtime (ts); - - if (ts != timestamp_nonexistent) - return t; - - level4 ([&]{trace << "no existing file for target " << t;}); - return nullptr; - } - default: - return t; - } - } - - recipe file_rule:: - apply (action a, target& t, const match_result&) const - { - // Update triggers the update of this target's prerequisites - // so it would seem natural that we should also trigger their - // cleanup. However, this possibility is rather theoretical - // since such an update would render this target out of date - // which in turn would lead to an error. So until we see a - // real use-case for this functionality, we simply ignore - // the clean operation. - // - if (a.operation () == clean_id) - return noop_recipe; - - // If we have no prerequisites, then this means this file - // is up to date. Return noop_recipe which will also cause - // the target's state to be set to unchanged. This is an - // important optimization on which quite a few places that - // deal with predominantly static content rely. - // - if (!t.has_prerequisites ()) - return noop_recipe; - - // Search and match all the prerequisites. - // - search_and_match_prerequisites (a, t); - return a == perform_update_id ? &perform_update : default_recipe; - } - - target_state file_rule:: - perform_update (action a, target& t) - { - // Make sure the target is not older than any of its prerequisites. - // - timestamp mt (dynamic_cast<path_target&> (t).mtime ()); - - for (target* pt: t.prerequisite_targets) - { - target_state ts (execute (a, *pt)); - - // If this is an mtime-based target, then compare timestamps. - // - if (auto mpt = dynamic_cast<const mtime_target*> (pt)) - { - timestamp mp (mpt->mtime ()); - - if (mt < mp) - fail << "no recipe to " << diag_do (a, t) << - info << "prerequisite " << *pt << " is ahead of " << t - << " by " << (mp - mt); - } - else - { - // Otherwise we assume the prerequisite is newer if it was changed. - // - if (ts == target_state::changed) - fail << "no recipe to " << diag_do (a, t) << - info << "prerequisite " << *pt << " is ahead of " << t - << " because it was updated"; - } - } - - return target_state::unchanged; - } - - file_rule file_rule::instance; - - // alias_rule - // - match_result alias_rule:: - match (action, target& t, const string&) const - { - return t; - } - - recipe alias_rule:: - apply (action a, target& t, const match_result&) const - { - search_and_match_prerequisites (a, t); - return default_recipe; - } - - alias_rule alias_rule::instance; - - // fsdir_rule - // - match_result fsdir_rule:: - match (action, target& t, const string&) const - { - return t; - } - - recipe fsdir_rule:: - apply (action a, target& t, const match_result&) const - { - // Inject dependency on the parent directory. Note that we - // don't do it for clean since we shouldn't be removing it. - // - if (a.operation () != clean_id) - inject_parent_fsdir (a, t); - - search_and_match_prerequisites (a, t); - - switch (a) - { - case perform_update_id: return &perform_update; - case perform_clean_id: return &perform_clean; - default: assert (false); return default_recipe; - } - } - - target_state fsdir_rule:: - perform_update (action a, target& t) - { - target_state ts (target_state::unchanged); - - // First update prerequisites (e.g. create parent directories) - // then create this directory. - // - if (!t.prerequisite_targets.empty ()) - ts = execute_prerequisites (a, t); - - const dir_path& d (t.dir); // Everything is in t.dir. - - // Generally, it is probably correct to assume that in the majority - // of cases the directory will already exist. If so, then we are - // going to get better performance by first checking if it indeed - // exists. See try_mkdir() for details. - // - if (!dir_exists (d)) - { - if (verb >= 2) - text << "mkdir " << d; - else if (verb) - text << "mkdir " << t; - - try - { - try_mkdir (d); - } - catch (const system_error& e) - { - fail << "unable to create directory " << d << ": " << e.what (); - } - - ts |= target_state::changed; - } - - return ts; - } - - target_state fsdir_rule:: - perform_clean (action a, target& t) - { - // The reverse order of update: first delete this directory, - // then clean prerequisites (e.g., delete parent directories). - // - // Don't fail if we couldn't remove the directory because it - // is not empty (or is current working directory). In this - // case rmdir() will issue a warning when appropriate. - // - target_state ts (rmdir (t.dir, t) - ? target_state::changed - : target_state::unchanged); - - if (!t.prerequisite_targets.empty ()) - ts |= reverse_execute_prerequisites (a, t); - - return ts; - } - - fsdir_rule fsdir_rule::instance; - - // fallback_rule - // - fallback_rule fallback_rule::instance; -} |