From 977d07a3ae47ef204665d1eda2d642e5064724f3 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 24 Jun 2019 12:01:19 +0200 Subject: Split build system into library and driver --- libbuild2/variable.txx | 670 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 670 insertions(+) create mode 100644 libbuild2/variable.txx (limited to 'libbuild2/variable.txx') 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 + +namespace build2 +{ + template + 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 + auto + convert (names&& ns) -> + decltype (value_traits::convert (move (ns[0]), nullptr)) + { + size_t n (ns.size ()); + + if (n == 0) + { + if (value_traits::empty_value) + return T (); + } + else if (n == 1) + { + return convert (move (ns[0])); + } + else if (n == 2 && ns[0].pair != '\0') + { + return convert (move (ns[0]), move (ns[1])); + } + + throw invalid_argument ( + string ("invalid ") + value_traits::type_name + + (n == 0 ? " value: empty" : " value: multiple names")); + } + + template + T + convert (value&& v) + { + if (v.type == nullptr) + return convert (move (v).as ()); + else if (v.type == &value_traits::value_type) + return move (v).as (); + + throw invalid_argument ( + string ("invalid ") + value_traits::value_type.name + + " value: conversion from " + v.type->name); + } + + template + void + default_dtor (value& v) + { + v.as ().~T (); + } + + template + void + default_copy_ctor (value& l, const value& r, bool m) + { + if (m) + new (&l.data_) T (move (const_cast (r).as ())); + else + new (&l.data_) T (r.as ()); + } + + template + void + default_copy_assign (value& l, const value& r, bool m) + { + if (m) + l.as () = move (const_cast (r).as ()); + else + l.as () = r.as (); + } + + template + bool + default_empty (const value& v) + { + return value_traits::empty (v.as ()); + } + + template + void + simple_assign (value& v, names&& ns, const variable* var) + { + size_t n (ns.size ()); + + if (value_traits::empty_value ? n <= 1 : n == 1) + { + try + { + value_traits::assign ( + v, + (n == 0 + ? T () + : value_traits::convert (move (ns.front ()), nullptr))); + + return; + } + catch (const invalid_argument&) {} // Fall through. + } + + diag_record dr (fail); + + dr << "invalid " << value_traits::value_type.name + << " value '" << ns << "'"; + + if (var != nullptr) + dr << " in variable " << var->name; + } + + template + void + simple_append (value& v, names&& ns, const variable* var) + { + size_t n (ns.size ()); + + if (value_traits::empty_value ? n <= 1 : n == 1) + { + try + { + value_traits::append ( + v, + (n == 0 + ? T () + : value_traits::convert (move (ns.front ()), nullptr))); + + return; + } + catch (const invalid_argument&) {} // Fall through. + } + + diag_record dr (fail); + + dr << "invalid " << value_traits::value_type.name + << " value '" << ns << "'"; + + if (var != nullptr) + dr << " in variable " << var->name; + } + + template + void + simple_prepend (value& v, names&& ns, const variable* var) + { + size_t n (ns.size ()); + + if (value_traits::empty_value ? n <= 1 : n == 1) + { + try + { + value_traits::prepend ( + v, + (n == 0 + ? T () + : value_traits::convert (move (ns.front ()), nullptr))); + + return; + } + catch (const invalid_argument&) {} // Fall through. + } + + diag_record dr (fail); + + dr << "invalid " << value_traits::value_type.name + << " value '" << ns << "'"; + + if (var != nullptr) + dr << " in variable " << var->name; + } + + template + names_view + simple_reverse (const value& v, names& s) + { + const T& x (v.as ()); + + // 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::empty (x)) + s.emplace_back (value_traits::reverse (x)); + + return s; + } + + template + int + simple_compare (const value& l, const value& r) + { + return value_traits::compare (l.as (), r.as ()); + } + + // vector value + // + + template + vector value_traits>:: + convert (names&& ns) + { + vector 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::convert (move (n), r)); + } + + return v; + } + + template + void + vector_append (value& v, names&& ns, const variable* var) + { + vector& p (v + ? v.as> () + : *new (&v.data_) vector ()); + + // 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::value_type.name << " value " + << "'" << n << "'" << n.pair << "'" << *r << "'"; + + if (var != nullptr) + dr << " in variable " << var->name; + } + } + + try + { + p.push_back (value_traits::convert (move (n), r)); + } + catch (const invalid_argument&) + { + diag_record dr (fail); + + dr << "invalid " << value_traits::value_type.name; + + if (n.pair) + dr << " element pair '" << n << "'@'" << *r << "'"; + else + dr << " element '" << n << "'"; + + if (var != nullptr) + dr << " in variable " << var->name; + } + } + } + + template + void + vector_assign (value& v, names&& ns, const variable* var) + { + if (v) + v.as> ().clear (); + + vector_append (v, move (ns), var); + } + + template + void + vector_prepend (value& v, names&& ns, const variable* var) + { + // Reduce to append. + // + vector t; + vector* p; + + if (v) + { + p = &v.as> (); + p->swap (t); + } + else + p = new (&v.data_) vector (); + + vector_append (v, move (ns), var); + + p->insert (p->end (), + make_move_iterator (t.begin ()), + make_move_iterator (t.end ())); + } + + template + static names_view + vector_reverse (const value& v, names& s) + { + auto& vv (v.as> ()); + s.reserve (vv.size ()); + + for (const T& x: vv) + s.push_back (value_traits::reverse (x)); + + return s; + } + + template + static int + vector_compare (const value& l, const value& r) + { + auto& lv (l.as> ()); + auto& rv (r.as> ()); + + 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::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 + value_traits>::value_type_ex:: + value_type_ex (value_type&& v) + : value_type (move (v)) + { + type_name = value_traits::type_name; + type_name += 's'; + name = type_name.c_str (); + } + + template + const vector value_traits>::empty_instance; + + template + const typename value_traits>::value_type_ex + value_traits>::value_type = build2::value_type // VC14 wants =. + { + nullptr, // Patched above. + sizeof (vector), + nullptr, // No base. + &value_traits::value_type, + &default_dtor>, + &default_copy_ctor>, + &default_copy_assign>, + &vector_assign, + &vector_append, + &vector_prepend, + &vector_reverse, + nullptr, // No cast (cast data_ directly). + &vector_compare, + &default_empty> + }; + + // map value + // + template + void + map_append (value& v, names&& ns, const variable* var) + { + using std::map; + + map& p (v + ? v.as> () + : *new (&v.data_) map ()); + + // 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>::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>::value_type.name << " key-value " + << "'" << l << "'" << l.pair << "'" << r << "'"; + + if (var != nullptr) + dr << " in variable " << var->name; + } + + try + { + K k (value_traits::convert (move (l), nullptr)); + + try + { + V v (value_traits::convert (move (r), nullptr)); + + p.emplace (move (k), move (v)); + } + catch (const invalid_argument&) + { + diag_record dr (fail); + + dr << "invalid " << value_traits::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::value_type.name + << " element key '" << l << "'"; + + if (var != nullptr) + dr << " in variable " << var->name; + } + } + } + + template + void + map_assign (value& v, names&& ns, const variable* var) + { + using std::map; + + if (v) + v.as> ().clear (); + + map_append (v, move (ns), var); + } + + template + static names_view + map_reverse (const value& v, names& s) + { + using std::map; + + auto& vm (v.as> ()); + s.reserve (2 * vm.size ()); + + for (const auto& p: vm) + { + s.push_back (value_traits::reverse (p.first)); + s.back ().pair = '@'; + s.push_back (value_traits::reverse (p.second)); + } + + return s; + } + + template + static int + map_compare (const value& l, const value& r) + { + using std::map; + + auto& lm (l.as> ()); + auto& rm (r.as> ()); + + 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::compare (li->first, ri->first)) != 0 || + (r = value_traits::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 + value_traits>::value_type_ex:: + value_type_ex (value_type&& v) + : value_type (move (v)) + { + type_name = value_traits::type_name; + type_name += '_'; + type_name += value_traits::type_name; + type_name += "_map"; + name = type_name.c_str (); + } + + template + const std::map value_traits>::empty_instance; + + template + const typename value_traits>::value_type_ex + value_traits>::value_type = build2::value_type // VC14 wants = + { + nullptr, // Patched above. + sizeof (map), + nullptr, // No base. + nullptr, // No element. + &default_dtor>, + &default_copy_ctor>, + &default_copy_assign>, + &map_assign, + &map_append, + &map_append, // Prepend is the same as append. + &map_reverse, + nullptr, // No cast (cast data_ directly). + &map_compare, + &default_empty> + }; + + // variable_cache + // + template + pair variable_cache:: + 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 (stem.value)->version + : 0); + + shared_mutex& m ( + variable_cache_mutex_shard[ + hash () (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 (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 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 (e.value, move (ul)); + } +} -- cgit v1.1