aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/operation.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/operation.hxx')
-rw-r--r--libbuild2/operation.hxx361
1 files changed, 361 insertions, 0 deletions
diff --git a/libbuild2/operation.hxx b/libbuild2/operation.hxx
new file mode 100644
index 0000000..86f93c6
--- /dev/null
+++ b/libbuild2/operation.hxx
@@ -0,0 +1,361 @@
+// file : libbuild2/operation.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBBUILD2_OPERATION_HXX
+#define LIBBUILD2_OPERATION_HXX
+
+#include <libbutl/string-table.mxx>
+
+#include <libbuild2/types.hxx>
+#include <libbuild2/utility.hxx>
+
+#include <libbuild2/action.hxx>
+#include <libbuild2/variable.hxx>
+#include <libbuild2/prerequisite.hxx>
+#include <libbuild2/target-state.hxx>
+
+#include <libbuild2/export.hxx>
+
+namespace build2
+{
+ class location;
+ class scope;
+ class target_key;
+ class target;
+ struct prerequisite_member;
+
+ struct opspec;
+
+ // Meta-operation info.
+ //
+
+ // Normally a list of resolved and matched targets to execute. But can be
+ // something else, depending on the meta-operation.
+ //
+ // The state is used to print structured result state. If it is not unknown,
+ // then this is assumed to be a target.
+ //
+ struct action_target
+ {
+ using target_type = build2::target;
+
+ const void* target = nullptr;
+ target_state state = target_state::unknown;
+
+ action_target () = default;
+ action_target (const void* t): target (t) {}
+
+ const target_type&
+ as_target () const {return *static_cast<const target_type*> (target);}
+ };
+
+ class action_targets: public vector<action_target>
+ {
+ public:
+ using vector<action_target>::vector;
+
+ void
+ reset () {for (auto& x: *this) x.state = target_state::unknown;}
+ };
+
+ struct meta_operation_info
+ {
+ const meta_operation_id id;
+ const string name;
+
+ // Name derivatives for diagnostics. If empty, then the meta-
+ // operation need not be mentioned.
+ //
+ const string name_do; // E.g., [to] 'configure'.
+ const string name_doing; // E.g., [while] 'configuring'.
+ const string name_did; // E.g., 'configured'.
+ const string name_done; // E.g., 'is configured'.
+
+ // Whether to bootstrap outer projects. If load() below calls load_root(),
+ // then this must be true. Note that this happens before
+ // meta_operation_pre() is called.
+ //
+ const bool bootstrap_outer;
+
+ // The first argument in all the callback is the meta-operation
+ // parameters.
+ //
+ // If the meta-operation expects parameters, then it should have a
+ // non-NULL meta_operation_pre(). Failed that, any parameters will be
+ // diagnosed as unexpected.
+
+ // Start of meta-operation and operation batches.
+ //
+ // 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) (const values&, const location&);
+ operation_id (*operation_pre) (const values&, operation_id);
+
+ // Meta-operation-specific logic to load the buildfile, search and match
+ // the targets, and execute the action on the targets.
+ //
+ void (*load) (const values&,
+ scope& root,
+ const path& buildfile,
+ const dir_path& out_base,
+ const dir_path& src_base,
+ const location&);
+
+ void (*search) (const values&,
+ const scope& root,
+ const scope& base,
+ const path& buildfile,
+ const target_key&,
+ const location&,
+ action_targets&);
+
+ // Diagnostics levels:
+ //
+ // 0 - none (for structured result).
+ // 1 - failures only (for pre-operations).
+ // 2 - all (for normal operations).
+ //
+ // The false progress argument can be used to suppress progress. If it is
+ // true, then whether the progress is shown is meta operation-specific (in
+ // other words, you can suppress it but not force it).
+ //
+ void (*match) (const values&, action, action_targets&,
+ uint16_t diag, bool progress);
+
+ void (*execute) (const values&, action, action_targets&,
+ uint16_t diag, bool progress);
+
+ // End of operation and meta-operation batches.
+ //
+ void (*operation_post) (const values&, operation_id);
+ void (*meta_operation_post) (const values&);
+
+ // Optional prerequisite inclusion/exclusion override callback. See
+ // include() for details.
+ //
+ include_type (*include) (action,
+ const target&,
+ const prerequisite_member&,
+ include_type);
+ };
+
+ // 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.
+ //
+ LIBBUILD2_SYMEXPORT void
+ load (const values&,
+ scope&,
+ const path&,
+ const dir_path&,
+ const dir_path&,
+ 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.
+ //
+ LIBBUILD2_SYMEXPORT void
+ search (const values&,
+ const scope&,
+ const scope&,
+ const path&,
+ const target_key&,
+ const location&,
+ action_targets&);
+
+ LIBBUILD2_SYMEXPORT void
+ match (const values&, action, action_targets&,
+ uint16_t diag, bool prog);
+
+ // Execute the action on the list of targets. This is the default
+ // implementation that does just that while issuing appropriate
+ // diagnostics (unless quiet).
+ //
+ LIBBUILD2_SYMEXPORT void
+ execute (const values&, action, const action_targets&,
+ uint16_t diag, bool prog);
+
+ LIBBUILD2_SYMEXPORT extern const meta_operation_info mo_noop;
+ LIBBUILD2_SYMEXPORT extern const meta_operation_info mo_perform;
+ LIBBUILD2_SYMEXPORT extern const meta_operation_info mo_info;
+
+ // Operation info.
+ //
+ // NOTE: keep POD-like to ensure can be constant-initialized in order to
+ // sidestep static initialization order (relied upon in operation
+ // aliasing).
+ //
+ struct operation_info
+ {
+ // If outer_id is not 0, then use that as the outer part of the
+ // action.
+ //
+ const operation_id id;
+ const operation_id outer_id;
+ const char* 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 char* name_do; // E.g., [to] 'update'.
+ const char* name_doing; // E.g., [while] 'updating'.
+ const char* name_did; // E.g., [not] 'updated'.
+ const char* name_done; // E.g., 'is up to date'.
+
+ const execution_mode mode;
+
+ // This is the operation's concurrency multiplier. 0 means run serially,
+ // 1 means run at hardware concurrency (unless overridden by the user).
+ //
+ const size_t concurrency;
+
+ // The first argument in all the callback is the operation parameters.
+ //
+ // If the operation expects parameters, then it should have a non-NULL
+ // pre(). Failed that, any parameters will be diagnosed as unexpected.
+
+ // 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) (const values&, meta_operation_id, const location&);
+ operation_id (*post) (const values&, meta_operation_id);
+ };
+
+ // Built-in operations.
+ //
+ LIBBUILD2_SYMEXPORT extern const operation_info op_default;
+ LIBBUILD2_SYMEXPORT extern const operation_info op_update;
+ LIBBUILD2_SYMEXPORT extern const operation_info op_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.
+ //
+ // A built-in/pre-defined meta-operation can also provide a pre-processor
+ // callback that will be called for operation-specs before any project
+ // discovery/bootstrap is performed.
+ //
+ struct meta_operation_data
+ {
+ // The processor may modify the parameters, opspec, and change the
+ // meta-operation by returning a different name.
+ //
+ // If lifted is true then the operation name in opspec is bogus (has
+ // been lifted) and the default/empty name should be assumed instead.
+ //
+ using process_func = const string& (const variable_overrides&,
+ values&,
+ vector_view<opspec>&,
+ bool lifted,
+ const location&);
+
+ meta_operation_data () = default;
+ meta_operation_data (const char* n, process_func p = nullptr)
+ : name (n), process (p) {}
+
+ string name;
+ process_func* process;
+ };
+
+ inline ostream&
+ operator<< (ostream& os, const meta_operation_data& d)
+ {
+ return os << d.name;
+ }
+
+ LIBBUILD2_SYMEXPORT extern butl::string_table<meta_operation_id,
+ meta_operation_data>
+ meta_operation_table;
+
+ LIBBUILD2_SYMEXPORT 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 = 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>;
+}
+
+namespace butl
+{
+ template <>
+ struct string_table_traits<build2::meta_operation_data>
+ {
+ static const std::string&
+ key (const build2::meta_operation_data& d) {return d.name;}
+ };
+}
+
+#endif // LIBBUILD2_OPERATION_HXX