diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2015-03-13 14:34:24 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2015-03-13 14:34:24 +0200 |
commit | ca41ca8f9a6b21588248e5fee1a013363f3f52a8 (patch) | |
tree | 6e791ddac1c6f794273a9701c0c7f1bc9ec3d000 /build/operation | |
parent | 0cee33621a93d3348a1bf19a0c94441b717cbcbc (diff) |
Add support for "first" and "last" execution modes
Diffstat (limited to 'build/operation')
-rw-r--r-- | build/operation | 67 |
1 files changed, 64 insertions, 3 deletions
diff --git a/build/operation b/build/operation index 3398f32..743458a 100644 --- a/build/operation +++ b/build/operation @@ -58,10 +58,71 @@ namespace build const action_id perform_update_id = (perform_id << 4) | update_id; const action_id perform_clean_id = (perform_id << 4) | clean_id; - // Meta/operation id tables. + // Recipe execution mode. // - using meta_operation_table = string_table<meta_operation_id>; - using operation_table = string_table<operation_id>; + // When a target is a prerequisite of another target, its recipe can be + // executed before the dependent's recipe (the normal case) or after. + // We will call these "front" and "back" execution modes, respectively + // (think "the prerequisite is 'front-running' the dependent"). + // + // There could also be several dependent targets and the prerequisite's + // recipe can be execute as part of the first dependent (the normal + // case) or last (or for all/some of them; see the recipe execution + // protocol in <target>). We will call these "first" and "last" + // execution modes, respectively. + // + // Now you may be having a hard time imagining where a mode other than + // the normal one (first/front) could be useful. An the answer is, + // compensating or inverse operations such as clean, uninstall, etc. + // If we use the last/back mode for, say, clean, then we will remove + // targets in the order inverse to the way they were updated. While + // this sounds like an elegant idea, are there any practical benefits + // of doing it this way. As it turns out there is (at least) one: when + // we are removing a directory (see fsdir{}), we want to do it after + // all the targets that depend on it (such as files, sub-directories) + // were removed. If we do it before, then the directory won't be empty + // yet. + // + // It appears that this execution mode is dictated by the essence of + // the operation. Constructive operations (those that "do") seem to + // naturally use the first/front mode. That is, we need to "do" the + // prerequisite first before we can "do" the dependent. While the + // destructive ones (those that "undo") seem to need last/back. That + // is, we need to "undo" all the dependents before we can "undo" the + // prerequisite (say, we need to remove all the files before we can + // remove their directory). + // + // If you noticed the parallel with the way C++ construction and + // destruction works for base/derived object then you earned a gold + // star! + // + // Note that the front/back mode is realized in the dependen's recipe + // (which is another indication that it is a property of the operation). + // + enum class execution_mode {first, last}; + + // Meta/operation tables. + // + struct meta_operation_info + { + const std::string name; + + const std::string& + key () const {return name;} // string_table interface. + }; + + struct operation_info + { + const std::string name; + const execution_mode mode; + + const std::string& + key () const {return name;} // string_table interface. + }; + + using meta_operation_table = string_table<meta_operation_id, + meta_operation_info>; + using operation_table = string_table<operation_id, operation_info>; extern meta_operation_table meta_operations; extern operation_table operations; |