aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/context.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/context.hxx')
-rw-r--r--libbuild2/context.hxx315
1 files changed, 241 insertions, 74 deletions
diff --git a/libbuild2/context.hxx b/libbuild2/context.hxx
index 6eb85f7..33fc892 100644
--- a/libbuild2/context.hxx
+++ b/libbuild2/context.hxx
@@ -21,7 +21,7 @@
namespace build2
{
class file_cache;
- class loaded_modules_lock;
+ class module_libraries_lock;
class LIBBUILD2_SYMEXPORT run_phase_mutex
{
@@ -120,6 +120,16 @@ namespace build2
}
};
+ // Match-only level.
+ //
+ // See the --match-only and --load-only options for background.
+ //
+ enum class match_only_level
+ {
+ alias, // Match only alias{} targets.
+ all // Match all targets.
+ };
+
// A build context encapsulates the state of a build. It is possible to have
// multiple build contexts provided they are non-overlapping, that is, they
// don't try to build the same projects (note that this is currently not
@@ -142,9 +152,9 @@ namespace build2
// instead go the multiple communicating schedulers route, a la the job
// server).
//
- // The loaded_modules state (module.hxx) is shared among all the contexts
+ // The module_libraries state (module.hxx) is shared among all the contexts
// (there is no way to have multiple shared library loading "contexts") and
- // is protected by loaded_modules_lock. A nested context should normally
+ // is protected by module_libraries_lock. A nested context should normally
// inherit this lock value from its outer context.
//
// Note also that any given thread should not participate in multiple
@@ -160,17 +170,66 @@ namespace build2
//
class LIBBUILD2_SYMEXPORT context
{
+ public:
+ // In order to perform each operation the build system goes through the
+ // following phases:
+ //
+ // load - load the buildfiles
+ // match - search prerequisites and match rules
+ // execute - execute the matched rule
+ //
+ // The build system starts with a "serial load" phase and then continues
+ // with parallel match and execute. Match, however, can be interrupted
+ // both with load and execute.
+ //
+ // Match can be interrupted with "exclusive load" in order to load
+ // additional buildfiles. Similarly, it can be interrupted with (parallel)
+ // execute in order to build targetd required to complete the match (for
+ // example, generated source code or source code generators themselves).
+ //
+ // Such interruptions are performed by phase change that is protected by
+ // phase_mutex (which is also used to synchronize the state changes
+ // between phases).
+ //
+ // Serial load can perform arbitrary changes to the build state. Exclusive
+ // load, however, can only perform "island appends". That is, it can
+ // create new "nodes" (variables, scopes, etc) but not (semantically)
+ // change already existing nodes or invalidate any references to such (the
+ // idea here is that one should be able to load additional buildfiles as
+ // long as they don't interfere with the existing build state). The
+ // "islands" are identified by the load_generation number (1 for the
+ // initial/serial load). It is incremented in case of a phase switch and
+ // can be stored in various "nodes" to verify modifications are only done
+ // "within the islands". Another example of invalidation would be
+ // insertion of a new scope "under" an existing target thus changing its
+ // scope hierarchy (and potentially even its base scope). This would be
+ // bad because we may have made decisions based on the original hierarchy,
+ // for example, we may have queried a variable which in the new hierarchy
+ // would "see" a new value from the newly inserted scope.
+ //
+ // The special load_generation value 0 indicates initialization before
+ // anything has been loaded. Currently, it is changed to 1 at the end
+ // of the context constructor.
+ //
+ // Note must come (and thus initialized) before the data_ member.
+ //
+ run_phase phase = run_phase::load;
+ size_t load_generation = 0;
+
+ private:
struct data;
unique_ptr<data> data_;
public:
- scheduler& sched;
- global_mutexes& mutexes;
- file_cache& fcache;
+ // These are only NULL for the "bare minimum" context (see below).
+ //
+ scheduler* sched;
+ global_mutexes* mutexes;
+ file_cache* fcache;
- // Match only flag (see --match-only but also dist).
+ // Match only flag/level (see --{load,match}-only but also dist).
//
- bool match_only;
+ optional<match_only_level> match_only;
// Skip booting external modules flag (see --no-external-modules).
//
@@ -211,6 +270,10 @@ namespace build2
bool dry_run = false;
bool dry_run_option;
+ // Diagnostics buffering flag (--no-diag-buffer).
+ //
+ bool no_diag_buffer;
+
// Keep going flag.
//
// Note that setting it to false is not of much help unless we are running
@@ -219,44 +282,13 @@ namespace build2
//
bool keep_going;
- // In order to perform each operation the build system goes through the
- // following phases:
+ // Targets to trace (see the --trace-* options).
//
- // load - load the buildfiles
- // match - search prerequisites and match rules
- // execute - execute the matched rule
- //
- // The build system starts with a "serial load" phase and then continues
- // with parallel match and execute. Match, however, can be interrupted
- // both with load and execute.
+ // Note that these must be set after construction and must remain valid
+ // for the lifetime of the context instance.
//
- // Match can be interrupted with "exclusive load" in order to load
- // additional buildfiles. Similarly, it can be interrupted with (parallel)
- // execute in order to build targetd required to complete the match (for
- // example, generated source code or source code generators themselves).
- //
- // Such interruptions are performed by phase change that is protected by
- // phase_mutex (which is also used to synchronize the state changes
- // between phases).
- //
- // Serial load can perform arbitrary changes to the build state. Exclusive
- // load, however, can only perform "island appends". That is, it can
- // create new "nodes" (variables, scopes, etc) but not (semantically)
- // change already existing nodes or invalidate any references to such (the
- // idea here is that one should be able to load additional buildfiles as
- // long as they don't interfere with the existing build state). The
- // "islands" are identified by the load_generation number (0 for the
- // initial/serial load). It is incremented in case of a phase switch and
- // can be stored in various "nodes" to verify modifications are only done
- // "within the islands". Another example of invalidation would be
- // insertion of a new scope "under" an existing target thus changing its
- // scope hierarchy (and potentially even its base scope). This would be
- // bad because we may have made decisions based on the original hierarchy,
- // for example, we may have queried a variable which in the new hierarchy
- // would "see" a new value from the newly inserted scope.
- //
- run_phase phase = run_phase::load;
- size_t load_generation = 0;
+ const vector<name>* trace_match = nullptr;
+ const vector<name>* trace_execute = nullptr;
// A "tri-mutex" that keeps all the threads in one of the three phases.
// When a thread wants to switch a phase, it has to wait for all the other
@@ -301,11 +333,6 @@ namespace build2
const operation_info* current_inner_oif;
const operation_info* current_outer_oif;
- // Current operation-specific variables.
- //
- const variable* current_inner_ovar;
- const variable* current_outer_ovar;
-
action
current_action () const
{
@@ -324,6 +351,22 @@ namespace build2
(current_mname.empty () && current_oname == mo));
};
+ // Meta/operation-specific context-global auxiliary data storage.
+ //
+ // Note: cleared by current_[meta_]operation() below. Normally set by
+ // meta/operation-specific callbacks from [mate_]operation_info.
+ //
+ // Note also: watch out for MT-safety in the data itself.
+ //
+ static void
+ null_current_data_deleter (void* p) { assert (p == nullptr); }
+
+ using current_data_ptr = unique_ptr<void, void (*) (void*)>;
+
+ current_data_ptr current_mdata = {nullptr, null_current_data_deleter};
+ current_data_ptr current_inner_odata = {nullptr, null_current_data_deleter};
+ current_data_ptr current_outer_odata = {nullptr, null_current_data_deleter};
+
// Current operation number (1-based) in the meta-operation batch.
//
size_t current_on;
@@ -362,26 +405,46 @@ namespace build2
// decremented after such recipe has been executed. If such a recipe has
// skipped executing the operation, then it should increment the skip
// count. These two counters are used for progress monitoring and
- // diagnostics.
+ // diagnostics. The resolve count keeps track of the number of targets
+ // matched but not executed as a result of the resolve_members() calls
+ // (see also target::resolve_counted).
//
atomic_count dependency_count;
atomic_count target_count;
atomic_count skip_count;
+ atomic_count resolve_count;
// Build state (scopes, targets, variables, etc).
//
const scope_map& scopes;
target_set& targets;
- const variable_pool& var_pool;
+ const variable_pool& var_pool; // Public variables pool.
+ const variable_patterns& var_patterns; // Public variables patterns.
const variable_overrides& var_overrides; // Project and relative scope.
function_map& functions;
- // Enter project-wide (as opposed to global) variable overrides.
+ // Current targets with post hoc prerequisites.
//
- void
- enter_project_overrides (scope& rs,
- const dir_path& out_base,
- const variable_overrides&);
+ // Note that we don't expect many of these so a simple mutex should be
+ // sufficient. Note also that we may end up adding more entries as we
+ // match existing so use list for node and iterator stability. See
+ // match_poshoc() for details.
+ //
+ struct posthoc_target
+ {
+ struct prerequisite_target
+ {
+ const build2::target* target;
+ uint64_t match_options;
+ };
+
+ build2::action action;
+ reference_wrapper<const build2::target> target;
+ vector<prerequisite_target> prerequisite_targets;
+ };
+
+ list<posthoc_target> current_posthoc_targets;
+ mutex current_posthoc_targets_mutex;
// Global scope.
//
@@ -390,6 +453,10 @@ namespace build2
variable_override_cache& global_override_cache;
const strings& global_var_overrides;
+ // Cached values (from global scope).
+ //
+ const target_triplet* build_host; // build.host
+
// Cached variables.
//
@@ -419,8 +486,8 @@ namespace build2
const variable* var_import_build2;
const variable* var_import_target;
- // The import.metadata variable and the --build2-metadata option are used
- // to pass the metadata compatibility version.
+ // The import.metadata export stub variable and the --build2-metadata
+ // executable option are used to pass the metadata compatibility version.
//
// This serves both as an indication that the metadata is required (can be
// useful, for example, in cases where it is expensive to calculate) as
@@ -432,7 +499,8 @@ namespace build2
// The export.metadata value should start with the version followed by the
// metadata variable prefix (for example, cli in cli.version).
//
- // The following metadata variable names have pre-defined meaning:
+ // The following metadata variable names have pre-defined meaning for
+ // executable targets (exe{}; see also process_path_ex):
//
// <var-prefix>.name = [string] # Stable name for diagnostics.
// <var-prefix>.version = [string] # Version for diagnostics.
@@ -442,7 +510,8 @@ namespace build2
// If the <var-prefix>.name variable is missing, it is set to the target
// name as imported.
//
- // See also process_path_ex.
+ // Note that the same mechanism is used for library user metadata (see
+ // cc::pkgconfig_{load,save}() for details).
//
const variable* var_import_metadata;
const variable* var_export_metadata;
@@ -473,18 +542,51 @@ namespace build2
//
const variable* var_clean;
- // Forwarded configuration backlink mode. Valid values are:
+ // Forwarded configuration backlink mode. The value has two components
+ // in the form:
+ //
+ // <mode> [<print>]
+ //
+ // Valid <mode> values are:
//
// false - no link.
// true - make a link using appropriate mechanism.
// symbolic - make a symbolic link.
// hard - make a hard link.
// copy - make a copy.
- // overwrite - copy over but don't remove on clean (committed gen code).
+ // overwrite - copy over but don't remove on clean.
+ // group - inherit the group mode (only valid for group members).
//
- // Note that it can be set by a matching rule as a rule-specific variable.
+ // While the <print> component should be either true or false and can be
+ // used to suppress printing of specific ad hoc group members at verbosity
+ // level 1. Note that it cannot be false for the primary member.
//
- // [string] target visibility
+ // Note that this value can be set by a matching rule as a rule-specific
+ // variable.
+ //
+ // Note also that the overwrite mode was originally meant for handling
+ // pregenerated source code. But in the end this did not pan out for
+ // the following reasons:
+ //
+ // 1. This would mean that the pregenerated and regenerated files end up
+ // in the same place (e.g., depending on the develop mode) and it's
+ // hard to make this work without resorting to a conditional graph.
+ //
+ // This could potentially be addressed by allowing backlink to specify
+ // a different location (similar to dist).
+ //
+ // 2. This support for pregenerated source code would be tied to forwarded
+ // configurations.
+ //
+ // Nevertheless, there may be a kernel of an idea here in that we may be
+ // able to provide a built-in "post-copy" mechanism which would allow one
+ // to have a pregenerated setup even when using non-ad hoc recipes
+ // (currently we just manually diff/copy stuff at the end of a recipe).
+ // (Or maybe we should stick to ad hoc recipes with post-diff/copy and
+ // just expose a mechanism to delegate to a different rule, which we
+ // already have).
+ //
+ // [names] target visibility
//
const variable* var_backlink;
@@ -512,7 +614,7 @@ namespace build2
// operations. The initial idea was to extend this value to allow
// specifying the operation (e.g., clean@false). However, later we
// realized that we could reuse the "operation-specific variables"
- // (update, clean, install, test; see context::current_ovar) with a more
+ // (update, clean, install, test; see project_operation_info) with a more
// natural-looking and composable result. Plus, this allows for
// operation-specific "modifiers", for example, "unmatch" and "update
// during match" logic for update (see var_update for details) or
@@ -565,9 +667,9 @@ namespace build2
dir_path old_src_root;
dir_path new_src_root;
- // NULL if this context hasn't already locked the loaded_modules state.
+ // NULL if this context hasn't already locked the module_libraries state.
//
- const loaded_modules_lock* modules_lock;
+ const module_libraries_lock* modules_lock;
// Nested context for updating build system modules and ad hoc recipes.
//
@@ -584,17 +686,76 @@ namespace build2
// properly setup context (including, normally, a self-reference in
// modules_context).
//
- explicit
+ // The var_override_function callback can be used to parse ad hoc project-
+ // wide variable overrides (see parse_variable_override()). This has to
+ // happen at a specific point during context construction (see the
+ // implementation for details).
+ //
+ // Note: see also the trace_* data members that, if needed, must be set
+ // separately, after construction.
+ //
+ struct reserves
+ {
+ size_t targets;
+ size_t variables;
+
+ reserves (): targets (0), variables (0) {}
+ reserves (size_t t, size_t v): targets (t), variables (v) {}
+ };
+
+ using var_override_function = void (context&, size_t&);
+
context (scheduler&,
global_mutexes&,
file_cache&,
- bool match_only = false,
+ optional<match_only_level> match_only = nullopt,
bool no_external_modules = false,
bool dry_run = false,
+ bool no_diag_buffer = false,
bool keep_going = true,
const strings& cmd_vars = {},
+ reserves = {0, 160},
optional<context*> module_context = nullptr,
- const loaded_modules_lock* inherited_mudules_lock = nullptr);
+ const module_libraries_lock* inherited_modules_lock = nullptr,
+ const function<var_override_function>& = nullptr);
+
+ // Special context with bare minimum of initializations. It is only
+ // guaranteed to be sufficiently initialized to call extract_variable().
+ //
+ // Note that for this purpose you may omit calls to init_diag() and
+ // init().
+ //
+ context ();
+
+ // Reserve elements in containers to avoid re-allocation/re-hashing. Zero
+ // values are ignored (that is, the corresponding container reserve()
+ // function is not called). Can only be called in the load phase.
+ //
+ void
+ reserve (reserves);
+
+ // Parse a variable override returning its type in the first half of the
+ // pair. Index is the variable index (used to derive unique name) and if
+ // buildspec is true then assume `--` is used as a separator between
+ // variables and buildscpec and issue appropriate diagnostics.
+ //
+ // Note: should only be called from the var_override_function constructor
+ // callback.
+ //
+ pair<char, variable_override>
+ parse_variable_override (const string& var, size_t index, bool buildspec);
+
+ // Enter project-wide (as opposed to global) variable overrides.
+ //
+ // If the amalgamation scope is specified, then use it instead of
+ // rs.weak_scope() to set overrides with global visibility (make sure you
+ // understand the implications before doing this).
+ //
+ void
+ enter_project_overrides (scope& rs,
+ const dir_path& out_base,
+ const variable_overrides&,
+ scope* amalgamation = nullptr);
// Set current meta-operation and operation.
//
@@ -688,14 +849,20 @@ namespace build2
//
struct LIBBUILD2_SYMEXPORT phase_unlock
{
- phase_unlock (context&, bool unlock = true, bool delay = false);
+ explicit phase_unlock (context*, bool delay = false);
+ explicit phase_unlock (context& ctx, bool delay = false)
+ : phase_unlock (&ctx, delay) {}
+
~phase_unlock () noexcept (false);
void
unlock ();
+ void
+ lock ();
+
context* ctx;
- phase_lock* lock;
+ phase_lock* lock_;
};
// Assuming we have a lock on the current phase, temporarily switch to a
@@ -741,8 +908,8 @@ namespace build2
// Note: move-assignable to empty only.
//
- wait_guard (wait_guard&&);
- wait_guard& operator= (wait_guard&&);
+ wait_guard (wait_guard&&) noexcept;
+ wait_guard& operator= (wait_guard&&) noexcept;
wait_guard (const wait_guard&) = delete;
wait_guard& operator= (const wait_guard&) = delete;