diff options
Diffstat (limited to 'build/operation')
-rw-r--r-- | build/operation | 359 |
1 files changed, 0 insertions, 359 deletions
diff --git a/build/operation b/build/operation deleted file mode 100644 index d8cbd07..0000000 --- a/build/operation +++ /dev/null @@ -1,359 +0,0 @@ -// file : build/operation -*- C++ -*- -// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD_OPERATION -#define BUILD_OPERATION - -#include <string> -#include <iosfwd> -#include <vector> -#include <cstdint> -#include <functional> // reference_wrapper - -#include <butl/string-table> - -#include <build/types> - -namespace build -{ - class location; - class scope; - class target_key; - - // While we are using uint8_t for the meta/operation ids, we assume - // that each is limited to 4 bits (max 128 entries) so that we can - // store the combined action id in uint8_t as well. This makes our - // life easier when it comes to defining switch labels for action - // ids (no need to mess with endian-ness). - // - // Note that 0 is not a valid meta/operation/action id. - // - using meta_operation_id = std::uint8_t; - using operation_id = std::uint8_t; - using action_id = std::uint8_t; - - // Meta-operations and operations are not the end of the story. We - // also have operation nesting (currently only one level deep) which - // is used to implement pre/post operations (currently, but may be - // useful for other things). Here is the idea: the test operation - // needs to make sure that the targets that it needs to test are - // up-to-date. So it runs update as its pre-operation. It is almost - // like an ordinary update except that it has test as its outer - // operation (the meta-operations are always the same). This way a - // rule can recognize that this is "update for test" and do something - // differently. For example, if an executable is not a test, then - // there is no use updating it. At the same time, most rules will - // ignore the fact that this is a nested update and for them it is - // "update as usual". - // - struct action - { - action (): inner_id (0), outer_id (0) {} // Invalid action. - - bool - valid () const {return inner_id != 0;} - - // If this is not a nested operation, then outer should be 0. - // - action (meta_operation_id m, operation_id inner, operation_id outer = 0) - : inner_id ((m << 4) | inner), - outer_id (outer == 0 ? 0 : (m << 4) | outer) {} - - meta_operation_id - meta_operation () const {return inner_id >> 4;} - - operation_id - operation () const {return inner_id & 0xF;} - - operation_id - outer_operation () const {return outer_id & 0xF;} - - // Implicit conversion operator to action_id for the switch() - // statement, etc. Most places will only care about the inner - // operation. - // - operator action_id () const {return inner_id;} - - action_id inner_id; - action_id outer_id; - }; - - // This is an "overrides" comparison, i.e., it returns true - // if the recipe for x overrides recipe for y. The idea is - // that for the same inner operation, action with an outer - // operation is "weaker" than the one without. - // - inline bool - operator> (action x, action y) - { - return x.inner_id != y.inner_id || - (x.outer_id != y.outer_id && y.outer_id != 0); - } - - // Note that these ignore the outer operation. - // - inline bool - operator== (action x, action y) {return x.inner_id == y.inner_id;} - - inline bool - operator!= (action x, action y) {return !(x == y);} - - std::ostream& - operator<< (std::ostream&, action); - - // Id constants for build-in and pre-defined meta/operations. - // - const meta_operation_id perform_id = 1; - const meta_operation_id configure_id = 2; - const meta_operation_id disfigure_id = 3; - const meta_operation_id dist_id = 4; - - // The default operation is a special marker that can be used to - // indicate that no operation was explicitly specified by the user. - // - const operation_id default_id = 1; // Shall be first. - const operation_id update_id = 2; - const operation_id clean_id = 3; - const operation_id test_id = 4; - const operation_id install_id = 5; - - const action_id perform_update_id = (perform_id << 4) | update_id; - const action_id perform_clean_id = (perform_id << 4) | clean_id; - const action_id perform_test_id = (perform_id << 4) | test_id; - const action_id perform_install_id = (perform_id << 4) | install_id; - - const action_id configure_update_id = (configure_id << 4) | update_id; - - // Recipe execution mode. - // - // 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 info. - // - - // Normally a list of resolved and matched targets to execute. But - // can be something else, depending on the meta-operation. - // - typedef std::vector<void*> action_targets; - - struct meta_operation_info - { - const std::string name; - - // Name derivatives for diagnostics. If empty, then the meta- - // operation need not be mentioned. - // - const std::string name_do; // E.g., [to] 'configure'. - const std::string name_doing; // E.g., [while] 'configuring'. - const std::string name_done; // E.g., 'is configured'. - - // If operation_pre() is not NULL, then it may translate default_id - // (and only default_id) to some other operation. If not translated, - // then default_id is used. If, however, operation_pre() is NULL, - // then default_id is translated to update_id. - // - void (*meta_operation_pre) (); // Start of meta-operation batch. - operation_id (*operation_pre) (operation_id); // Start of operation batch. - - // Meta-operation-specific logic to load the buildfile, search and match - // the targets, and execute the action on the targets. - // - void (*load) (const path& buildfile, - scope& root, - const dir_path& out_base, - const dir_path& src_base, - const location&); - - void (*search) (scope& root, - const target_key&, - const location&, - action_targets&); - - void (*match) (action, action_targets&); - - void (*execute) (action, const action_targets&, bool quiet); - - void (*operation_post) (operation_id); // End of operation batch. - void (*meta_operation_post) (); // End of meta-operation batch. - }; - - // Built-in meta-operations. - // - - // perform - // - - // Load the buildfile. This is the default implementation that first - // calls root_pre(), then creates the scope for out_base, and, finally, - // loads the buildfile unless it has already been loaded for the root - // scope. - // - void - load (const path& buildfile, - scope& root, - const dir_path& out_base, - const dir_path& src_base, - const location&); - - // Search and match the target. This is the default implementation - // that does just that and adds a pointer to the target to the list. - // - void - search (scope&, const target_key&, const location&, action_targets&); - - void - match (action, action_targets&); - - // Execute the action on the list of targets. This is the default - // implementation that does just that while issuing appropriate - // diagnostics (unless quiet). - // - void - execute (action, const action_targets&, bool quiet); - - extern meta_operation_info perform; - - // Operation info. - // - struct operation_info - { - const std::string name; - - // Name derivatives for diagnostics. Note that unlike meta-operations, - // these can only be empty for the default operation (id 1), And - // meta-operations that make use of the default operation shall not - // have empty derivatives (failed which only target name will be - // printed). - // - const std::string name_do; // E.g., [to] 'update'. - const std::string name_doing; // E.g., [while] 'updating'. - const std::string name_done; // E.g., 'is up to date'. - - const execution_mode mode; - - // If the returned operation_id's are not 0, then they are injected - // as pre/post operations for this operation. Can be NULL if unused. - // The returned operation_id shall not be default_id. - // - operation_id (*pre) (meta_operation_id); - operation_id (*post) (meta_operation_id); - }; - - // Built-in operations. - // - extern operation_info default_; - extern operation_info update; - extern operation_info clean; - - // Global meta/operation tables. Each registered meta/operation - // is assigned an id which is used as an index in the per-project - // registered meta/operation lists. - // - // We have three types of meta/operations: built-in (e.g., perform, - // update), pre-defined (e.g., configure, test), and dynamically- - // defined. For built-in ones, both the id and implementation are - // part of the build2 core. For pre-defined, the id is registered - // as part of the core but the implementation is loaded as part of - // a module. The idea with pre-defined operations is that they have - // common, well-established semantics but could still be optional. - // Another aspect of pre-defined operations is that often rules - // across multiple modules need to know their ids. Finally, - // dynamically-defined meta/operations have their ids registered - // as part of a module load. In this case, the meta/operation is - // normally (but not necessarily) fully implemented by this module. - // - // Note also that the name of a meta/operation in a sense defines - // its semantics. It would be strange to have an operation called - // test that does two very different things in different projects. - // - extern butl::string_table<meta_operation_id> meta_operation_table; - extern butl::string_table<operation_id> operation_table; - - // These are "sparse" in the sense that we may have "holes" that - // are represented as NULL pointers. Also, lookup out of bounds - // is treated as a hole. - // - template <typename T> - struct sparse_vector - { - using base_type = std::vector<T*>; - using size_type = typename base_type::size_type; - - void - insert (size_type i, T& x) - { - size_type n (v_.size ()); - - if (i < n) - v_[i] = &x; - else - { - if (n != i) - v_.resize (i, nullptr); // Add holes. - v_.push_back (&x); - } - } - - T* - operator[] (size_type i) const - { - return i < v_.size () ? v_[i] : nullptr; - } - - bool - empty () const {return v_.empty ();} - - // Note that this is more of a "max index" rather than size. - // - size_type - size () const {return v_.size ();} - - private: - base_type v_; - }; - - using meta_operations = sparse_vector<const meta_operation_info>; - using operations = sparse_vector<const operation_info>; -} - -#endif // BUILD_OPERATION |