diff options
Diffstat (limited to 'libbuild2/variable.hxx')
-rw-r--r-- | libbuild2/variable.hxx | 690 |
1 files changed, 554 insertions, 136 deletions
diff --git a/libbuild2/variable.hxx b/libbuild2/variable.hxx index c0f0fd9..eebb767 100644 --- a/libbuild2/variable.hxx +++ b/libbuild2/variable.hxx @@ -4,18 +4,22 @@ #ifndef LIBBUILD2_VARIABLE_HXX #define LIBBUILD2_VARIABLE_HXX -#include <type_traits> // aligned_storage +#include <cstddef> // max_align_t +#include <type_traits> // is_* #include <unordered_map> -#include <libbutl/prefix-map.mxx> -#include <libbutl/multi-index.mxx> // map_key +#include <libbutl/prefix-map.hxx> +#include <libbutl/multi-index.hxx> // map_key #include <libbuild2/types.hxx> #include <libbuild2/forward.hxx> #include <libbuild2/utility.hxx> +#include <libbuild2/json.hxx> + #include <libbuild2/context.hxx> #include <libbuild2/target-type.hxx> +#include <libbuild2/diagnostics.hxx> #include <libbuild2/export.hxx> @@ -47,7 +51,11 @@ namespace build2 template <typename T> const value_type* is_a () const; - // Element type, if this is a vector. + // True if the type is a container. + // + bool container; + + // Element type, if this is a container and the element type is named. // const value_type* element_type; @@ -74,14 +82,19 @@ namespace build2 void (*const prepend) (value&, names&&, const variable*); // Reverse the value back to a vector of names. Storage can be used by the - // implementation if necessary. Cannot be NULL. + // implementation if necessary. If reduce is true, then for an empty + // simple value return an empty list rather than a list of one empty name. + // Note that the value cannot be NULL. // - names_view (*const reverse) (const value&, names& storage); + names_view (*const reverse) (const value&, names& storage, bool reduce); // Cast value::data_ storage to value type so that the result can be // static_cast to const T*. If it is NULL, then cast data_ directly. Note // that this function is used for both const and non-const values. // + // @@ This is currently ignored by as<T>() which is now used in quite a + // few places (in particular, grep for as<T>). + // const void* (*const cast) (const value&, const value_type*); // If NULL, then the types are compared as PODs using memcmp(). @@ -90,7 +103,33 @@ namespace build2 // If NULL, then the value is never empty. // + // Note that this is "semantically empty", not necessarily + // "representationally empty". For example, an empty JSON array is + // semantically empty but its representation (`[]`) is not. + // bool (*const empty) (const value&); + + // Custom subscript function. If NULL, then the generic implementation is + // used. + // + // Note that val can be NULL. If val_data points to val, then it can be + // moved from. The sloc and bloc arguments are the subscript and brace + // locations, respectively. + // + // Note: should normally be consistent with iterate. + // + value (*/*const*/ subscript) (const value& val, + value* val_data, + value&& subscript, + const location& sloc, + const location& bloc); + + // Custom iteration function. It should invoked the specified function for + // each element in order. If NULL, then the generic implementation is + // used. The passed value is never NULL. + // + void (*const iterate) (const value&, + const function<void (value&&, bool first)>&); }; // The order of the enumerators is arranged so that their integral values @@ -106,6 +145,10 @@ namespace build2 scope, // This scope (no outer scopes). target, // Target and target type/pattern-specific. prereq // Prerequisite-specific. + + // Note: remember to update the visibility attribute parsing if adding any + // new values here. As well as the $builtin.visibility() function + // documentation. }; // VC14 reports ambiguity but seems to work if we don't provide any. @@ -145,13 +188,27 @@ namespace build2 return o << to_string (v); } - // variable + // A variable. + // + // A variable can be public, project-private, or script-private, which + // corresponds to the variable pool it belongs to (see variable_pool). The + // two variables from the same pool are considered the same if they have the + // same name. The variable access (public/private) rules are: + // + // - Qualified variable are by default public while unqualified -- private. + // + // - Private must have project or lesser visibility and not be overridable. + // + // - An unqualified public variable can only be pre-entered during the + // context construction (to make sure it is not entered as private). // - // The two variables are considered the same if they have the same name. + // - There is no scope-private variables in our model due to side-loading, + // target type/pattern-specific append, etc. // // Variables can be aliases of each other in which case they form a circular // linked list (the aliases pointer for variable without any aliases points - // to the variable itself). + // to the variable itself). This mechanism should only be used for variables + // of the same access (normally public). // // If the variable is overridden on the command line, then override is the // linked list of the special override variables. Their names are derived @@ -198,6 +255,7 @@ namespace build2 struct variable { string name; + const variable_pool* owner; const variable* aliases; // Circular linked list. const value_type* type; // If NULL, then not (yet) typed. unique_ptr<const variable> overrides; @@ -276,7 +334,13 @@ namespace build2 // Extra data that is associated with the value that can be used to store // flags, etc. It is initialized to 0 and copied (but not assigned) from // one value to another but is otherwise untouched (not even when the - // value is reset to NULL). + // value is reset to NULL) unless it is part of variable_map::value_data, + // in which case it is reset to 0 on each modification (version + // increment; however, see reset_extra flag in variable_map::insert()). + // + // (The reset on each modification semantics is used to implement the + // default value distinction as currently done in the config module but + // later probably will be done for ?= and $origin()). // // Note: if deciding to use for something make sure it is not overlapping // with an existing usage. @@ -290,6 +354,10 @@ namespace build2 // Check in a type-independent way if the value is empty. The value must // not be NULL. // + // Note that this is "semantically empty", not necessarily + // "representationally empty". For example, an empty JSON array is + // semantically empty but its representation (`[]`) is not. + // bool empty () const; @@ -327,9 +395,13 @@ namespace build2 value& operator= (nullptr_t) {if (!null) reset (); return *this;} - value (value&&); + // Note that we have the noexcept specification even though copy_ctor() + // could potentially throw (for example, for std::map). + // + value (value&&) noexcept; + explicit value (const value&); - value& operator= (value&&); + value& operator= (value&&); // Note: can throw for untyped RHS. value& operator= (const value&); value& operator= (reference_wrapper<value>); value& operator= (reference_wrapper<const value>); @@ -338,8 +410,8 @@ namespace build2 // public: // Assign/append/prepend a typed value. For assign, LHS should be either - // of the same type or untyped. For append, LHS should be either of the - // same type or untyped and NULL. + // of the same type or untyped. For append/prepend, LHS should be either + // of the same type or untyped and NULL. // template <typename T> value& operator= (T); template <typename T> value& operator+= (T); @@ -388,8 +460,8 @@ namespace build2 // specialization below). Types that don't fit will have to be handled // with an extra dynamic allocation. // - static constexpr size_t size_ = sizeof (name_pair); - std::aligned_storage<size_>::type data_; + static constexpr size_t size_ = sizeof (name_pair); + alignas (std::max_align_t) unsigned char data_[size_]; // Make sure we have sufficient storage for untyped values. // @@ -429,38 +501,37 @@ namespace build2 template <typename T> T& cast (value&); template <typename T> T&& cast (value&&); template <typename T> const T& cast (const value&); - template <typename T> const T& cast (const lookup&); + template <typename T> const T& cast (lookup); // As above but returns NULL if the value is NULL (or not defined, in // case of lookup). // template <typename T> T* cast_null (value&); template <typename T> const T* cast_null (const value&); - template <typename T> const T* cast_null (const lookup&); + template <typename T> const T* cast_null (lookup); // As above but returns empty value if the value is NULL (or not defined, in // case of lookup). // template <typename T> const T& cast_empty (const value&); - template <typename T> const T& cast_empty (const lookup&); + template <typename T> const T& cast_empty (lookup); // As above but returns the specified default if the value is NULL (or not // defined, in case of lookup). Note that the return is by value, not by // reference. // template <typename T> T cast_default (const value&, const T&); - template <typename T> T cast_default (const lookup&, const T&); + template <typename T> T cast_default (lookup, const T&); // As above but returns false/true if the value is NULL (or not defined, // in case of lookup). Note that the template argument is only for // documentation and should be bool (or semantically compatible). // template <typename T> T cast_false (const value&); - template <typename T> T cast_false (const lookup&); + template <typename T> T cast_false (lookup); template <typename T> T cast_true (const value&); - template <typename T> T cast_true (const lookup&); - + template <typename T> T cast_true (lookup); // Assign value type to the value. The variable is optional and is only used // for diagnostics. @@ -473,20 +544,22 @@ namespace build2 typify_atomic (context&, value&, const value_type&, const variable*); // Remove value type from the value reversing it to names. This is similar - // to reverse() below except that it modifies the value itself. + // to reverse() below except that it modifies the value itself. Note that + // the reduce semantics applies to empty but not null. // - LIBBUILD2_SYMEXPORT void untypify (value&); + LIBBUILD2_SYMEXPORT void untypify (value&, bool reduce); // Reverse the value back to names. The value should not be NULL and storage - // should be empty. + // should be empty. If reduce is true, then for an empty simple value return + // an empty list rather than a list of one empty name. // vector_view<const name> - reverse (const value&, names& storage); + reverse (const value&, names& storage, bool reduce); vector_view<name> - reverse (value&, names& storage); + reverse (value&, names& storage, bool reduce); - // Variable lookup result, AKA, binding of a name to a value. + // Variable lookup result, AKA, binding of a variable to a value. // // A variable can be undefined, NULL, or contain a (potentially empty) // value. @@ -539,6 +612,15 @@ namespace build2 vars (v != nullptr ? m : nullptr) {} }; + // Variable lookup limit (see {scope,target}::lookup_original()). + // + enum class lookup_limit + { + none, + target_type, + target + }; + // Two lookups are equal if they point to the same variable. // inline bool @@ -629,17 +711,21 @@ namespace build2 // case (container) if invalid_argument is thrown, the names are not // guaranteed to be unchanged. // - //template <typename T> T convert (names&&); (declaration causes ambiguity) + template <typename T> T convert (names&&); // Convert value to T. If value is already of type T, then simply cast it. - // Otherwise call convert(names) above. If value is NULL, then throw - // invalid_argument (with an appropriate message). + // Otherwise call convert(names) above. If the value is NULL, then throw + // invalid_argument (with an appropriate message). See also + // convert_to_base() below. // template <typename T> T convert (value&&); + template <typename T> T convert (const value&); - // As above but preserving the value. + // As above but also allow the derived-to-base conversions (where T is + // base). Note that this call may potentially slice the value. // - template <typename T> T convert (const value&); + template <typename T> T convert_to_base (value&&); + template <typename T> T convert_to_base (const value&); // Default implementations of the dtor/copy_ctor/copy_assing callbacks for // types that are stored directly in value::data_ and the provide all the @@ -907,7 +993,7 @@ namespace build2 // pair of two empties). // // @@ Maybe we should redo this with optional<> to signify which half can - // be missing? + // be missing? See also dump_value(json). // template <> struct LIBBUILD2_SYMEXPORT value_traits<name_pair> @@ -1115,12 +1201,35 @@ namespace build2 static const pair_vector_value_type<K, V> value_type; }; + // set<T> + // + template <typename T> + struct set_value_type; + + template <typename T> + struct value_traits<set<T>> + { + static_assert (sizeof (set<T>) <= value::size_, "insufficient space"); + + static set<T> convert (names&&); + static void assign (value&, set<T>&&); + static void append (value&, set<T>&&); + static void prepend (value&, set<T>&&); + static bool empty (const set<T>& x) {return x.empty ();} + + static const set<T> empty_instance; + static const set_value_type<T> value_type; + }; + // map<K, V> // // Either K or V can be optional<T> making the key or value optional. // - // Note that append/+= is non-overriding (like insert()) while prepend/=+ - // is (like insert_or_assign()). + // Note that append/+= is overriding (like insert_or_assign()) while + // prepend/=+ is not (like insert()). In a sense, whatever appears last + // (from left to right) is kept, which is consistent with what we expect to + // happen when specifying the same key repeatedly in a representation (e.g., + // a@0 a@1). // template <typename K, typename V> struct map_value_type; @@ -1141,12 +1250,116 @@ namespace build2 static const map_value_type<K, V> value_type; }; + // json + // + // Note that we do not expose json_member as a value type instead + // representing it as an object with one member. While we could expose + // member (and reverse it as a pair since there is no valid JSON + // representation for a standalone member), this doesn't seem to buy us much + // but will cause complications (for example, in supporting append/prepend). + // On the other hand, representing a member as an object only requires a bit + // of what looks like harmless looseness in a few contexts (such as the + // $json.member_*() functions). + // + // Note that similar to map, JSON object append/+= is overriding while + // prepend/=+ is not. In a sense, whatever appears last (from left to right) + // is kept, which is consistent with what we expect to happen when + // specifying the same name repeatedly (provided it's not considered + // invalid) in a representation (e.g., {"a":1,"a":2}). + // + template <> + struct LIBBUILD2_SYMEXPORT value_traits<json_value> + { + static_assert (sizeof (json_value) <= value::size_, "insufficient space"); + + static json_value convert (names&&); + static void assign (value&, json_value&&); + static void append (value&, json_value&&); + static void prepend (value&, json_value&&); + static bool empty (const json_value&); // null or empty array/object + + // These are provided to make it possible to use json_value as a container + // element. + // + static json_value convert (name&&, name*); + static name reverse (const json_value&); + static int compare (const json_value& x, const json_value& y) { + return x.compare (y);} + + static const json_value empty_instance; // null + static const char* const type_name; + static const build2::value_type value_type; + }; + + template <> + struct LIBBUILD2_SYMEXPORT value_traits<json_array> + { + static_assert (sizeof (json_array) <= value::size_, "insufficient space"); + + static json_array convert (names&&); + static void assign (value&, json_array&&); + static void append (value&, json_value&&); // Note: value, not array. + static void prepend (value&, json_value&&); + static bool empty (const json_array& v) {return v.array.empty ();} + + static const json_array empty_instance; // empty array + static const char* const type_name; + static const build2::value_type value_type; + }; + + template <> + struct LIBBUILD2_SYMEXPORT value_traits<json_object> + { + static_assert (sizeof (json_object) <= value::size_, "insufficient space"); + + static json_object convert (names&&); + static void assign (value&, json_object&&); + static void append (value&, json_value&&); // Note: value, not object. + static void prepend (value&, json_value&&); + static bool empty (const json_object& v) {return v.object.empty ();} + + static const json_object empty_instance; // empty object + static const char* const type_name; + static const build2::value_type value_type; + }; + + // Canned command line to be re-lexed (used in {Build,Test}scripts). + // + // Note that because the executable can be specific as a target or as + // process_path_ex, this is a list of names rather than a list of strings. + // Note also that unlike vector<name> this type allows name pairs. + // + struct cmdline: vector<name> + { + using vector<name>::vector; + + cmdline () {} // For Clang. + }; + + template <> + struct LIBBUILD2_SYMEXPORT value_traits<cmdline> + { + static_assert (sizeof (cmdline) <= value::size_, "insufficient space"); + + static cmdline convert (names&&); + static void assign (value&, cmdline&&); + static void append (value&, cmdline&&); + static void prepend (value&, cmdline&&); + static bool empty (const cmdline& x) {return x.empty ();} + + static const cmdline empty_instance; + static const char* const type_name; + static const build2::value_type value_type; + }; + // Explicitly pre-instantiate and export value_traits templates for // vector/map value types used in the build2 project. Note that this is not // merely an optimization since not doing so we may end up with multiple // value type objects for the same traits type (and we use their addressed // as identity; see cast(const value&) for an example). // + // NOTE: REMEMBER TO UPDATE dump_value(json) IF CHANGING ANYTHING HERE! + // extern template struct LIBBUILD2_DECEXPORT value_traits<strings>; extern template struct LIBBUILD2_DECEXPORT value_traits<vector<name>>; extern template struct LIBBUILD2_DECEXPORT value_traits<paths>; @@ -1166,10 +1379,16 @@ namespace build2 extern template struct LIBBUILD2_DECEXPORT value_traits<vector<pair<string, optional<bool>>>>; + extern template struct LIBBUILD2_DECEXPORT value_traits<set<string>>; + extern template struct LIBBUILD2_DECEXPORT value_traits<set<json_value>>; + extern template struct LIBBUILD2_DECEXPORT value_traits<map<string, string>>; extern template struct LIBBUILD2_DECEXPORT + value_traits<map<json_value, json_value>>; + + extern template struct LIBBUILD2_DECEXPORT value_traits<map<string, optional<string>>>; extern template struct LIBBUILD2_DECEXPORT @@ -1196,9 +1415,12 @@ namespace build2 // Variable pool. // - // The global (as in, context-wide) version is protected by the phase mutex. + // The shared versions (as in, context or project-wide) are protected by the + // phase mutex and thus can only be modified during the load phase. // - class variable_pool + class variable_patterns; + + class LIBBUILD2_SYMEXPORT variable_pool { public: // Find existing (assert exists). @@ -1218,7 +1440,7 @@ namespace build2 // // Note also that a pattern and later insertions may restrict (but not // relax) visibility and overridability. - + // const variable& insert (string name) { @@ -1276,6 +1498,12 @@ namespace build2 } const variable& + insert (string name, const value_type* type) + { + return insert (move (name), type, nullptr, nullptr).first; + } + + const variable& insert (string name, const value_type* type, bool overridable, @@ -1296,70 +1524,74 @@ namespace build2 // Overridable aliased variables are most likely a bad idea: without a // significant effort, the overrides will only be applied along the alias // names (i.e., there would be no cross-alias overriding). So for now we - // don't allow this (use the common variable mechanism instead). + // don't allow this (manually handle multiple names by merging their + // values instead). + // + // Note: currently only public variables can be aliased. // - LIBBUILD2_SYMEXPORT const variable& + const variable& insert_alias (const variable& var, string name); - // Insert a variable pattern. Any variable that matches this pattern will - // have the specified type, visibility, and overridability. If match is - // true, then individual insertions of the matching variable must match - // the specified type/visibility/overridability. Otherwise, individual - // insertions can provide alternative values and the pattern values are a - // fallback (if you specify false you better be very clear about what you - // are trying to achieve). + // Iteration. // - // The pattern must be in the form [<prefix>.](*|**)[.<suffix>] where '*' - // matches single component stems (i.e., 'foo' but not 'foo.bar') and '**' - // matches single and multi-component stems. Note that only multi- - // component variables are considered for pattern matching (so just '*' - // won't match anything). + public: + using key = butl::map_key<string>; + using map = std::unordered_map<key, variable>; + + using const_iterator = butl::map_iterator_adapter<map::const_iterator>; + + const_iterator begin () const {return const_iterator (map_.begin ());} + const_iterator end () const {return const_iterator (map_.end ());} + + // Construction. // - // The patterns are matched in the more-specific-first order where the - // pattern is considered more specific if it has a greater sum of its - // prefix and suffix lengths. If the prefix and suffix are equal, then the - // '*' pattern is considered more specific than '**'. If neither is more - // specific, then they are matched in the reverse order of insertion. + // There are three specific variable pool instances: // - // If retro is true then a newly inserted pattern is also applied - // retrospectively to all the existing variables that match but only - // if no more specific pattern already exists (which is then assumed - // to have been applied). So if you use this functionality, watch out - // for the insertion order (you probably want more specific first). + // shared outer + // ---------------- + // true null -- public variable pool in context + // true not null -- project-private pool in scope::root_extra + // with outer pointing to context::var_pool + // false not null -- temporary scope-private pool in temp_scope + // with outer pointing to context::var_pool + // false null -- script-private pool in script::environment // - public: - LIBBUILD2_SYMEXPORT void - insert_pattern (const string& pattern, - optional<const value_type*> type, - optional<bool> overridable, - optional<variable_visibility>, - bool retro = false, - bool match = true); + // Notice that the script-private pool doesn't rely on outer and does + // its own pool chaining. So currently we assume that if outer is not + // NULL, then this is a project-private pool. + // + private: + friend class context; + friend class temp_scope; - template <typename T> - void - insert_pattern (const string& p, - optional<bool> overridable, - optional<variable_visibility> v, - bool retro = false, - bool match = true) - { - insert_pattern ( - p, &value_traits<T>::value_type, overridable, v, retro, match); - } + // Shared pool (public or project-private). The shared argument is + // flag/context. + // + variable_pool (context* shared, + variable_pool* outer, + const variable_patterns* patterns) + : shared_ (shared), outer_ (outer), patterns_ (patterns) {} public: - void - clear () {map_.clear ();} + // Script-private pool. + // + explicit + variable_pool (const variable_patterns* patterns = nullptr) + : shared_ (nullptr), outer_ (nullptr), patterns_ (patterns) {} - variable_pool (): variable_pool (nullptr) {} + variable_pool (variable_pool&&) = delete; + variable_pool& operator= (variable_pool&&) = delete; - // RW access (only for the global pool). + variable_pool (const variable_pool&) = delete; + variable_pool& operator= (const variable_pool&) = delete; + + public: + // RW access (only for shared pools plus the temp_scope special case). // variable_pool& rw () const { - assert (global_->phase == run_phase::load); + assert (shared_ == nullptr || shared_->phase == run_phase::load); return const_cast<variable_pool&> (*this); } @@ -1372,19 +1604,19 @@ namespace build2 friend class scope; private: - friend class parser; - // Note that in insert() NULL overridable is interpreted as false unless // overridden by a pattern while in update() NULL overridable is ignored. // - LIBBUILD2_SYMEXPORT pair<variable&, bool> + pair<variable&, bool> insert (string name, const value_type*, const variable_visibility*, const bool* overridable, bool pattern = true); - LIBBUILD2_SYMEXPORT void + // Note: the variable must belong to this pool. + // + void update (variable&, const value_type*, const variable_visibility*, @@ -1393,9 +1625,6 @@ namespace build2 // Variable map. // private: - using key = butl::map_key<string>; - using map = std::unordered_map<key, variable>; - pair<map::iterator, bool> insert (variable&& var) { @@ -1404,19 +1633,127 @@ namespace build2 // gets hairy very quickly (there is no std::hash for C-strings). So // let's rely on small object-optimized std::string for now. // - string n (var.name); + string n (var.name); // @@ PERF (maybe keep reuse buffer at least?) auto r (map_.insert (map::value_type (&n, move (var)))); if (r.second) + { +#if 0 + if (shared_ && outer_ == nullptr) // Global pool in context. + { + size_t n (map_.bucket_count ()); + if (n > buckets_) + { + text << "variable_pool buckets: " << buckets_ << " -> " << n + << " (" << map_.size () << ")"; + buckets_ = n; + } + } +#endif r.first->first.p = &r.first->second.name; + } return r; } + private: + friend class variable_patterns; + + context* shared_; + variable_pool* outer_; + const variable_patterns* patterns_; map map_; - // Patterns. +#if 0 + size_t buckets_ = 0; +#endif + }; + + // Variable patterns. + // + // This mechanism is used to assign variable types/visibility/overridability + // based on the variable name pattern. This mechanism can only be used for + // qualified variables and is thus only provided for the public variable + // pool. + // + // Similar to variable_pool, the shared versions are protected by the phase + // mutex and thus can only be modified during the load phase. + // + class LIBBUILD2_SYMEXPORT variable_patterns + { + public: + // Insert a variable pattern. Any variable that matches this pattern will + // have the specified type, visibility, and overridability. If match is + // true, then individual insertions of the matching variable must match + // the specified type/visibility/overridability. Otherwise, individual + // insertions can provide alternative values and the pattern values are a + // fallback (if you specify false you better be very clear about what you + // are trying to achieve). + // + // The pattern must be in the form [<prefix>.](*|**)[.<suffix>] where '*' + // matches single component stems (i.e., 'foo' but not 'foo.bar') and '**' + // matches single and multi-component stems. Note that only multi- + // component variables are considered for pattern matching (so just '*' + // won't match anything). + // + // The patterns are matched in the more-specific-first order where the + // pattern is considered more specific if it has a greater sum of its + // prefix and suffix lengths. If the prefix and suffix are equal, then the + // '*' pattern is considered more specific than '**'. If neither is more + // specific, then they are matched in the reverse order of insertion. + // + // If retro is true then a newly inserted pattern is also applied + // retrospectively to all the existing variables that match but only + // if no more specific pattern already exists (which is then assumed + // to have been applied). So if you use this functionality, watch out + // for the insertion order (you probably want more specific first). // + void + insert (const string& pattern, + optional<const value_type*> type, + optional<bool> overridable, + optional<variable_visibility>, + bool retro = false, + bool match = true); + + template <typename T> + void + insert (const string& p, + optional<bool> overridable, + optional<variable_visibility> v, + bool retro = false, + bool match = true) + { + insert (p, &value_traits<T>::value_type, overridable, v, retro, match); + } + + public: + // The shared argument is flag/context. The pool argument is for + // retrospective pattern application. + // + explicit + variable_patterns (context* shared, variable_pool* pool) + : shared_ (shared), pool_ (pool) {} + + variable_patterns (variable_patterns&&) = delete; + variable_patterns& operator= (variable_patterns&&) = delete; + + variable_patterns (const variable_patterns&) = delete; + variable_patterns& operator= (const variable_patterns&) = delete; + + public: + // RW access (only for shared pools). + // + variable_patterns& + rw () const + { + assert (shared_->phase == run_phase::load); + return const_cast<variable_patterns&> (*this); + } + + variable_patterns& + rw (scope&) const {return const_cast<variable_patterns&> (*this);} + public: struct pattern { @@ -1444,17 +1781,11 @@ namespace build2 }; private: - multiset<pattern> patterns_; - - // Global pool flag/context. - // - private: - friend class context; + friend class variable_pool; - explicit - variable_pool (context* global): global_ (global) {} - - context* global_; + context* shared_; + variable_pool* pool_; + multiset<pattern> patterns_; }; } @@ -1495,7 +1826,10 @@ namespace build2 using value::value; using value::operator=; - size_t version = 0; // Incremented on each modification (variable_cache). + // Incremented on each modification, at which point we also reset + // value::extra to 0. + // + size_t version = 0; }; // Note that we guarantee ascending iteration order (e.g., for predictable @@ -1537,8 +1871,13 @@ namespace build2 lookup_type operator[] (const variable& var) const { - auto p (lookup (var)); - return lookup_type (p.first, &p.second, this); + lookup_type r; + if (!empty ()) + { + auto p (lookup (var)); + r = lookup_type (p.first, &p.second, this); + } + return r; } lookup_type @@ -1551,17 +1890,24 @@ namespace build2 lookup_type operator[] (const string& name) const { - const variable* var (ctx != nullptr - ? ctx->var_pool.find (name) - : nullptr); - return var != nullptr ? operator[] (*var) : lookup_type (); + assert (owner_ != owner::context); + + lookup_type r; + if (!empty ()) + r = lookup (name); + return r; } - // If typed is false, leave the value untyped even if the variable is. - // The second half of the pair is the storage variable. + lookup_type + lookup (const string& name) const; + + // If typed is false, leave the value untyped even if the variable is. If + // aliased is false, then don't consider aliases (used by the variable + // override machinery where the aliases chain is repurrposed for something + // else). The second half of the pair is the storage variable. // pair<const value_data*, const variable&> - lookup (const variable&, bool typed = true) const; + lookup (const variable&, bool typed = true, bool aliased = true) const; pair<value_data*, const variable&> lookup_to_modify (const variable&, bool typed = true); @@ -1574,6 +1920,18 @@ namespace build2 const_iterator (r.second, *this)); } + pair<const_iterator, const_iterator> + lookup_namespace (string ns) const + { + // It's ok to use the temporary here since we compare names and don't + // insert anything. + // + return lookup_namespace (variable { + move (ns), + nullptr, nullptr, nullptr, nullptr, + variable_visibility::project}); + } + // Convert a lookup pointing to a value belonging to this variable map // to its non-const version. Note that this is only safe on the original // values (see lookup_original()). @@ -1583,6 +1941,7 @@ namespace build2 { assert (l.vars == this); value& r (const_cast<value&> (*l.value)); + r.extra = 0; static_cast<value_data&> (r).version++; return r; } @@ -1599,24 +1958,37 @@ namespace build2 return assign (*var); } - // Note that the variable is expected to have already been registered. + // Note that the variable is expected to have already been inserted. // value& - assign (const string& name) {return insert (ctx->var_pool[name]).first;} + assign (const string& name); // As above but also return an indication of whether the new value (which // will be NULL) was actually inserted. Similar to find(), if typed is - // false, leave the value untyped even if the variable is. + // false, leave the value untyped even if the variable is. If reset_extra + // is false, then don't reset the existing value's value::extra. // pair<value&, bool> - insert (const variable&, bool typed = true); + insert (const variable&, bool typed = true, bool reset_extra = true); - // Note: does not deal with aliases. + // Note: the following functions do not deal with aliases. // + const_iterator + find (const variable& var) const + { + return const_iterator (m_.find (var), *this); + } + + const_iterator + find (const string& name) const; + bool erase (const variable&); const_iterator + erase (const_iterator); + + const_iterator begin () const {return const_iterator (m_.begin (), *this);} const_iterator @@ -1629,21 +2001,58 @@ namespace build2 size () const {return m_.size ();} public: - // Global should be true if this map is part of the global build state - // (e.g., scopes, etc). + // Shared should be true if this map is part of the shared build state + // (e.g., scopes) and thus should only be modified during the load phase. // explicit - variable_map (context& c, bool global = false) - : ctx (&c), global_ (global) {} + variable_map (const scope& owner, bool shared = false); + + explicit + variable_map (const target& owner, bool shared = false); + + explicit + variable_map (const prerequisite& owner, bool shared = false); + + variable_map (variable_map&&, const prerequisite&, bool shared = false); + variable_map (const variable_map&, const prerequisite&, bool shared = false); + + variable_map& + operator= (variable_map&& v) noexcept {m_ = move (v.m_); return *this;} + + variable_map& + operator= (const variable_map& v) {m_ = v.m_; return *this;} + + // The context owner is for special "managed" variable maps. Note that + // such maps cannot lookup/insert variable names specified as strings. + // + variable_map (context& c, bool shared) + : shared_ (shared), owner_ (owner::context), ctx (&c) {} + + // Note: std::map's move constructor can throw. + // + variable_map (variable_map&& v) + : shared_ (v.shared_), owner_ (v.owner_), ctx (v.ctx), m_ (move (v.m_)) + { + assert (owner_ == owner::context); + } + + variable_map (const variable_map& v) + : shared_ (v.shared_), owner_ (v.owner_), ctx (v.ctx), m_ (v.m_) + { + assert (v.owner_ == owner::context); + } void clear () {m_.clear ();} - // Implementation details (only used for empty_variable_map). + // Implementation details. // public: + enum class owner {empty, context, scope, target, prereq}; + explicit - variable_map (context* c): ctx (c) {} + variable_map (owner o, context* c = nullptr, bool shared = false) + : shared_ (shared), owner_ (o), ctx (c) {} private: friend class variable_type_map; @@ -1652,9 +2061,18 @@ namespace build2 typify (const value_data&, const variable&) const; private: + friend class target_set; + + bool shared_; + owner owner_; + union + { + const scope* scope_; + const target* target_; + const prerequisite* prereq_; + }; context* ctx; map_type m_; - bool global_; }; LIBBUILD2_SYMEXPORT extern const variable_map empty_variable_map; @@ -1787,8 +2205,8 @@ namespace build2 using const_iterator = map_type::const_iterator; using const_reverse_iterator = map_type::const_reverse_iterator; - variable_pattern_map (context& c, bool global) - : ctx (c), global_ (global) {} + variable_pattern_map (context& c, bool shared) + : ctx (c), shared_ (shared) {} // Note that here we assume the "outer" pattern format (delimiters, flags, // etc) is valid. @@ -1804,7 +2222,7 @@ namespace build2 operator[] (string text) { return map_.emplace (pattern {pattern_type::path, false, move (text), {}}, - variable_map (ctx, global_)).first->second; + variable_map (ctx, shared_)).first->second; } const_iterator begin () const {return map_.begin ();} @@ -1816,7 +2234,7 @@ namespace build2 private: context& ctx; map_type map_; - bool global_; + bool shared_; }; class LIBBUILD2_SYMEXPORT variable_type_map @@ -1826,13 +2244,13 @@ namespace build2 variable_pattern_map>; using const_iterator = map_type::const_iterator; - variable_type_map (context& c, bool global): ctx (c), global_ (global) {} + variable_type_map (context& c, bool shared): ctx (c), shared_ (shared) {} variable_pattern_map& operator[] (const target_type& t) { return map_.emplace ( - t, variable_pattern_map (ctx, global_)).first->second; + t, variable_pattern_map (ctx, shared_)).first->second; } const_iterator begin () const {return map_.begin ();} @@ -1862,7 +2280,7 @@ namespace build2 private: context& ctx; map_type map_; - bool global_; + bool shared_; }; } |