aboutsummaryrefslogtreecommitdiff
path: root/build/operation
diff options
context:
space:
mode:
Diffstat (limited to 'build/operation')
-rw-r--r--build/operation359
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