aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/variable.txx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/variable.txx')
-rw-r--r--libbuild2/variable.txx670
1 files changed, 670 insertions, 0 deletions
diff --git a/libbuild2/variable.txx b/libbuild2/variable.txx
new file mode 100644
index 0000000..9b7490a
--- /dev/null
+++ b/libbuild2/variable.txx
@@ -0,0 +1,670 @@
+// file : libbuild2/variable.txx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <libbuild2/diagnostics.hxx>
+
+namespace build2
+{
+ template <typename T>
+ bool lookup::
+ belongs (const T& x, bool t) const
+ {
+ if (vars == &x.vars)
+ return true;
+
+ if (t)
+ {
+ for (const auto& p1: x.target_vars) // variable_type_map
+ {
+ for (const auto& p2: p1.second) // variable_pattern_map
+ {
+ if (vars == &p2.second)
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // This one will be SFINAE'd out unless T is a simple value.
+ //
+ template <typename T>
+ auto
+ convert (names&& ns) ->
+ decltype (value_traits<T>::convert (move (ns[0]), nullptr))
+ {
+ size_t n (ns.size ());
+
+ if (n == 0)
+ {
+ if (value_traits<T>::empty_value)
+ return T ();
+ }
+ else if (n == 1)
+ {
+ return convert<T> (move (ns[0]));
+ }
+ else if (n == 2 && ns[0].pair != '\0')
+ {
+ return convert<T> (move (ns[0]), move (ns[1]));
+ }
+
+ throw invalid_argument (
+ string ("invalid ") + value_traits<T>::type_name +
+ (n == 0 ? " value: empty" : " value: multiple names"));
+ }
+
+ template <typename T>
+ T
+ convert (value&& v)
+ {
+ if (v.type == nullptr)
+ return convert<T> (move (v).as<names> ());
+ else if (v.type == &value_traits<T>::value_type)
+ return move (v).as<T> ();
+
+ throw invalid_argument (
+ string ("invalid ") + value_traits<T>::value_type.name +
+ " value: conversion from " + v.type->name);
+ }
+
+ template <typename T>
+ void
+ default_dtor (value& v)
+ {
+ v.as<T> ().~T ();
+ }
+
+ template <typename T>
+ void
+ default_copy_ctor (value& l, const value& r, bool m)
+ {
+ if (m)
+ new (&l.data_) T (move (const_cast<value&> (r).as<T> ()));
+ else
+ new (&l.data_) T (r.as<T> ());
+ }
+
+ template <typename T>
+ void
+ default_copy_assign (value& l, const value& r, bool m)
+ {
+ if (m)
+ l.as<T> () = move (const_cast<value&> (r).as<T> ());
+ else
+ l.as<T> () = r.as<T> ();
+ }
+
+ template <typename T>
+ bool
+ default_empty (const value& v)
+ {
+ return value_traits<T>::empty (v.as<T> ());
+ }
+
+ template <typename T>
+ void
+ simple_assign (value& v, names&& ns, const variable* var)
+ {
+ size_t n (ns.size ());
+
+ if (value_traits<T>::empty_value ? n <= 1 : n == 1)
+ {
+ try
+ {
+ value_traits<T>::assign (
+ v,
+ (n == 0
+ ? T ()
+ : value_traits<T>::convert (move (ns.front ()), nullptr)));
+
+ return;
+ }
+ catch (const invalid_argument&) {} // Fall through.
+ }
+
+ diag_record dr (fail);
+
+ dr << "invalid " << value_traits<T>::value_type.name
+ << " value '" << ns << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ }
+
+ template <typename T>
+ void
+ simple_append (value& v, names&& ns, const variable* var)
+ {
+ size_t n (ns.size ());
+
+ if (value_traits<T>::empty_value ? n <= 1 : n == 1)
+ {
+ try
+ {
+ value_traits<T>::append (
+ v,
+ (n == 0
+ ? T ()
+ : value_traits<T>::convert (move (ns.front ()), nullptr)));
+
+ return;
+ }
+ catch (const invalid_argument&) {} // Fall through.
+ }
+
+ diag_record dr (fail);
+
+ dr << "invalid " << value_traits<T>::value_type.name
+ << " value '" << ns << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ }
+
+ template <typename T>
+ void
+ simple_prepend (value& v, names&& ns, const variable* var)
+ {
+ size_t n (ns.size ());
+
+ if (value_traits<T>::empty_value ? n <= 1 : n == 1)
+ {
+ try
+ {
+ value_traits<T>::prepend (
+ v,
+ (n == 0
+ ? T ()
+ : value_traits<T>::convert (move (ns.front ()), nullptr)));
+
+ return;
+ }
+ catch (const invalid_argument&) {} // Fall through.
+ }
+
+ diag_record dr (fail);
+
+ dr << "invalid " << value_traits<T>::value_type.name
+ << " value '" << ns << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ }
+
+ template <typename T>
+ names_view
+ simple_reverse (const value& v, names& s)
+ {
+ const T& x (v.as<T> ());
+
+ // Represent an empty simple value as empty name sequence rather than
+ // a single empty name. This way, for example, during serialization we
+ // end up with a much saner looking:
+ //
+ // config.import.foo =
+ //
+ // Rather than:
+ //
+ // config.import.foo = {}
+ //
+ if (!value_traits<T>::empty (x))
+ s.emplace_back (value_traits<T>::reverse (x));
+
+ return s;
+ }
+
+ template <typename T>
+ int
+ simple_compare (const value& l, const value& r)
+ {
+ return value_traits<T>::compare (l.as<T> (), r.as<T> ());
+ }
+
+ // vector<T> value
+ //
+
+ template <typename T>
+ vector<T> value_traits<vector<T>>::
+ convert (names&& ns)
+ {
+ vector<T> v;
+
+ // Similar to vector_append() below except we throw instead of issuing
+ // diagnostics.
+ //
+ for (auto i (ns.begin ()); i != ns.end (); ++i)
+ {
+ name& n (*i);
+ name* r (nullptr);
+
+ if (n.pair)
+ {
+ r = &*++i;
+
+ if (n.pair != '@')
+ throw invalid_argument (
+ string ("invalid pair character: '") + n.pair + "'");
+ }
+
+ v.push_back (value_traits<T>::convert (move (n), r));
+ }
+
+ return v;
+ }
+
+ template <typename T>
+ void
+ vector_append (value& v, names&& ns, const variable* var)
+ {
+ vector<T>& p (v
+ ? v.as<vector<T>> ()
+ : *new (&v.data_) vector<T> ());
+
+ // Convert each element to T while merging pairs.
+ //
+ for (auto i (ns.begin ()); i != ns.end (); ++i)
+ {
+ name& n (*i);
+ name* r (nullptr);
+
+ if (n.pair)
+ {
+ r = &*++i;
+
+ if (n.pair != '@')
+ {
+ diag_record dr (fail);
+
+ dr << "unexpected pair style for "
+ << value_traits<T>::value_type.name << " value "
+ << "'" << n << "'" << n.pair << "'" << *r << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ }
+ }
+
+ try
+ {
+ p.push_back (value_traits<T>::convert (move (n), r));
+ }
+ catch (const invalid_argument&)
+ {
+ diag_record dr (fail);
+
+ dr << "invalid " << value_traits<T>::value_type.name;
+
+ if (n.pair)
+ dr << " element pair '" << n << "'@'" << *r << "'";
+ else
+ dr << " element '" << n << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ }
+ }
+ }
+
+ template <typename T>
+ void
+ vector_assign (value& v, names&& ns, const variable* var)
+ {
+ if (v)
+ v.as<vector<T>> ().clear ();
+
+ vector_append<T> (v, move (ns), var);
+ }
+
+ template <typename T>
+ void
+ vector_prepend (value& v, names&& ns, const variable* var)
+ {
+ // Reduce to append.
+ //
+ vector<T> t;
+ vector<T>* p;
+
+ if (v)
+ {
+ p = &v.as<vector<T>> ();
+ p->swap (t);
+ }
+ else
+ p = new (&v.data_) vector<T> ();
+
+ vector_append<T> (v, move (ns), var);
+
+ p->insert (p->end (),
+ make_move_iterator (t.begin ()),
+ make_move_iterator (t.end ()));
+ }
+
+ template <typename T>
+ static names_view
+ vector_reverse (const value& v, names& s)
+ {
+ auto& vv (v.as<vector<T>> ());
+ s.reserve (vv.size ());
+
+ for (const T& x: vv)
+ s.push_back (value_traits<T>::reverse (x));
+
+ return s;
+ }
+
+ template <typename T>
+ static int
+ vector_compare (const value& l, const value& r)
+ {
+ auto& lv (l.as<vector<T>> ());
+ auto& rv (r.as<vector<T>> ());
+
+ auto li (lv.begin ()), le (lv.end ());
+ auto ri (rv.begin ()), re (rv.end ());
+
+ for (; li != le && ri != re; ++li, ++ri)
+ if (int r = value_traits<T>::compare (*li, *ri))
+ return r;
+
+ if (li == le && ri != re) // l shorter than r.
+ return -1;
+
+ if (ri == re && li != le) // r shorter than l.
+ return 1;
+
+ return 0;
+ }
+
+ template <typename T>
+ value_traits<vector<T>>::value_type_ex::
+ value_type_ex (value_type&& v)
+ : value_type (move (v))
+ {
+ type_name = value_traits<T>::type_name;
+ type_name += 's';
+ name = type_name.c_str ();
+ }
+
+ template <typename T>
+ const vector<T> value_traits<vector<T>>::empty_instance;
+
+ template <typename T>
+ const typename value_traits<vector<T>>::value_type_ex
+ value_traits<vector<T>>::value_type = build2::value_type // VC14 wants =.
+ {
+ nullptr, // Patched above.
+ sizeof (vector<T>),
+ nullptr, // No base.
+ &value_traits<T>::value_type,
+ &default_dtor<vector<T>>,
+ &default_copy_ctor<vector<T>>,
+ &default_copy_assign<vector<T>>,
+ &vector_assign<T>,
+ &vector_append<T>,
+ &vector_prepend<T>,
+ &vector_reverse<T>,
+ nullptr, // No cast (cast data_ directly).
+ &vector_compare<T>,
+ &default_empty<vector<T>>
+ };
+
+ // map<K, V> value
+ //
+ template <typename K, typename V>
+ void
+ map_append (value& v, names&& ns, const variable* var)
+ {
+ using std::map;
+
+ map<K, V>& p (v
+ ? v.as<map<K, V>> ()
+ : *new (&v.data_) map<K, V> ());
+
+ // Verify we have a sequence of pairs and convert each lhs/rhs to K/V.
+ //
+ for (auto i (ns.begin ()); i != ns.end (); ++i)
+ {
+ name& l (*i);
+
+ if (!l.pair)
+ {
+ diag_record dr (fail);
+
+ dr << value_traits<map<K, V>>::value_type.name << " key-value "
+ << "pair expected instead of '" << l << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ }
+
+ name& r (*++i); // Got to have the second half of the pair.
+
+ if (l.pair != '@')
+ {
+ diag_record dr (fail);
+
+ dr << "unexpected pair style for "
+ << value_traits<map<K, V>>::value_type.name << " key-value "
+ << "'" << l << "'" << l.pair << "'" << r << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ }
+
+ try
+ {
+ K k (value_traits<K>::convert (move (l), nullptr));
+
+ try
+ {
+ V v (value_traits<V>::convert (move (r), nullptr));
+
+ p.emplace (move (k), move (v));
+ }
+ catch (const invalid_argument&)
+ {
+ diag_record dr (fail);
+
+ dr << "invalid " << value_traits<V>::value_type.name
+ << " element value '" << r << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ }
+ }
+ catch (const invalid_argument&)
+ {
+ diag_record dr (fail);
+
+ dr << "invalid " << value_traits<K>::value_type.name
+ << " element key '" << l << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ }
+ }
+ }
+
+ template <typename K, typename V>
+ void
+ map_assign (value& v, names&& ns, const variable* var)
+ {
+ using std::map;
+
+ if (v)
+ v.as<map<K, V>> ().clear ();
+
+ map_append<K, V> (v, move (ns), var);
+ }
+
+ template <typename K, typename V>
+ static names_view
+ map_reverse (const value& v, names& s)
+ {
+ using std::map;
+
+ auto& vm (v.as<map<K, V>> ());
+ s.reserve (2 * vm.size ());
+
+ for (const auto& p: vm)
+ {
+ s.push_back (value_traits<K>::reverse (p.first));
+ s.back ().pair = '@';
+ s.push_back (value_traits<V>::reverse (p.second));
+ }
+
+ return s;
+ }
+
+ template <typename K, typename V>
+ static int
+ map_compare (const value& l, const value& r)
+ {
+ using std::map;
+
+ auto& lm (l.as<map<K, V>> ());
+ auto& rm (r.as<map<K, V>> ());
+
+ auto li (lm.begin ()), le (lm.end ());
+ auto ri (rm.begin ()), re (rm.end ());
+
+ for (; li != le && ri != re; ++li, ++ri)
+ {
+ int r;
+ if ((r = value_traits<K>::compare (li->first, ri->first)) != 0 ||
+ (r = value_traits<V>::compare (li->second, ri->second)) != 0)
+ return r;
+ }
+
+ if (li == le && ri != re) // l shorter than r.
+ return -1;
+
+ if (ri == re && li != le) // r shorter than l.
+ return 1;
+
+ return 0;
+ }
+
+ template <typename K, typename V>
+ value_traits<std::map<K, V>>::value_type_ex::
+ value_type_ex (value_type&& v)
+ : value_type (move (v))
+ {
+ type_name = value_traits<K>::type_name;
+ type_name += '_';
+ type_name += value_traits<V>::type_name;
+ type_name += "_map";
+ name = type_name.c_str ();
+ }
+
+ template <typename K, typename V>
+ const std::map<K,V> value_traits<std::map<K, V>>::empty_instance;
+
+ template <typename K, typename V>
+ const typename value_traits<std::map<K, V>>::value_type_ex
+ value_traits<std::map<K, V>>::value_type = build2::value_type // VC14 wants =
+ {
+ nullptr, // Patched above.
+ sizeof (map<K, V>),
+ nullptr, // No base.
+ nullptr, // No element.
+ &default_dtor<map<K, V>>,
+ &default_copy_ctor<map<K, V>>,
+ &default_copy_assign<map<K, V>>,
+ &map_assign<K, V>,
+ &map_append<K, V>,
+ &map_append<K, V>, // Prepend is the same as append.
+ &map_reverse<K, V>,
+ nullptr, // No cast (cast data_ directly).
+ &map_compare<K, V>,
+ &default_empty<map<K, V>>
+ };
+
+ // variable_cache
+ //
+ template <typename K>
+ pair<value&, ulock> variable_cache<K>::
+ insert (K k, const lookup& stem, size_t ver, const variable& var)
+ {
+ using value_data = variable_map::value_data;
+
+ const variable_map* svars (stem.vars); // NULL if undefined.
+ size_t sver (stem.defined ()
+ ? static_cast<const value_data*> (stem.value)->version
+ : 0);
+
+ shared_mutex& m (
+ variable_cache_mutex_shard[
+ hash<variable_cache*> () (this) % variable_cache_mutex_shard_size]);
+
+ slock sl (m);
+ ulock ul (m, defer_lock);
+
+ auto i (m_.find (k));
+
+ // Cache hit.
+ //
+ if (i != m_.end () &&
+ i->second.version == ver &&
+ i->second.stem_vars == svars &&
+ i->second.stem_version == sver &&
+ (var.type == nullptr || i->second.value.type == var.type))
+ return pair<value&, ulock> (i->second.value, move (ul));
+
+ // Relock for exclusive access. Note that it is entirely possible
+ // that between unlock and lock someone else has updated the entry.
+ //
+ sl.unlock ();
+ ul.lock ();
+
+ // Note that the cache entries are never removed so we can reuse the
+ // iterator.
+ //
+ pair<typename map_type::iterator, bool> p (i, i == m_.end ());
+
+ if (p.second)
+ p = m_.emplace (move (k),
+ entry_type {value_data (nullptr), ver, svars, sver});
+
+ entry_type& e (p.first->second);
+
+ if (p.second)
+ {
+ // Cache miss.
+ //
+ e.value.version++; // New value.
+ }
+ else if (e.version != ver ||
+ e.stem_vars != svars ||
+ e.stem_version != sver)
+ {
+ // Cache invalidation.
+ //
+ assert (e.version <= ver);
+ e.version = ver;
+
+ if (e.stem_vars != svars)
+ e.stem_vars = svars;
+ else
+ assert (e.stem_version <= sver);
+
+ e.stem_version = sver;
+
+ e.value.version++; // Value changed.
+ }
+ else
+ {
+ // Cache hit.
+ //
+ if (var.type != nullptr && e.value.type != var.type)
+ typify (e.value, *var.type, &var);
+
+ ul.unlock ();
+ }
+
+ return pair<value&, ulock> (e.value, move (ul));
+ }
+}