diff options
Diffstat (limited to 'libbuild2/context.hxx')
-rw-r--r-- | libbuild2/context.hxx | 315 |
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; |