aboutsummaryrefslogtreecommitdiff
path: root/build2/variable.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'build2/variable.cxx')
-rw-r--r--build2/variable.cxx1522
1 files changed, 0 insertions, 1522 deletions
diff --git a/build2/variable.cxx b/build2/variable.cxx
deleted file mode 100644
index 173efe5..0000000
--- a/build2/variable.cxx
+++ /dev/null
@@ -1,1522 +0,0 @@
-// file : build2/variable.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build2/variable.hxx>
-
-#include <cstring> // memcmp()
-
-#include <libbutl/filesystem.mxx> // path_match()
-
-#include <build2/context.hxx>
-#include <build2/diagnostics.hxx>
-
-using namespace std;
-
-namespace build2
-{
- // variable_visibility
- //
- ostream&
- operator<< (ostream& o, variable_visibility v)
- {
- const char* s (nullptr);
-
- switch (v)
- {
- case variable_visibility::normal: s = "normal"; break;
- case variable_visibility::project: s = "project"; break;
- case variable_visibility::scope: s = "scope"; break;
- case variable_visibility::target: s = "target"; break;
- case variable_visibility::prereq: s = "prerequisite"; break;
- }
-
- return o << s;
- }
-
- // value
- //
- void value::
- reset ()
- {
- if (type == nullptr)
- as<names> ().~names ();
- else if (type->dtor != nullptr)
- type->dtor (*this);
-
- null = true;
- }
-
- value::
- value (value&& v)
- : type (v.type), null (v.null), extra (v.extra)
- {
- if (!null)
- {
- if (type == nullptr)
- new (&data_) names (move (v).as<names> ());
- else if (type->copy_ctor != nullptr)
- type->copy_ctor (*this, v, true);
- else
- data_ = v.data_; // Copy as POD.
- }
- }
-
- value::
- value (const value& v)
- : type (v.type), null (v.null), extra (v.extra)
- {
- if (!null)
- {
- if (type == nullptr)
- new (&data_) names (v.as<names> ());
- else if (type->copy_ctor != nullptr)
- type->copy_ctor (*this, v, false);
- else
- data_ = v.data_; // Copy as POD.
- }
- }
-
- value& value::
- operator= (value&& v)
- {
- if (this != &v)
- {
- // Prepare the receiving value.
- //
- if (type != v.type)
- {
- *this = nullptr;
- type = v.type;
- }
-
- // Now our types are the same. If the receiving value is NULL, then call
- // copy_ctor() instead of copy_assign().
- //
- if (v)
- {
- if (type == nullptr)
- {
- if (null)
- new (&data_) names (move (v).as<names> ());
- else
- as<names> () = move (v).as<names> ();
- }
- else if (auto f = null ? type->copy_ctor : type->copy_assign)
- f (*this, v, true);
- else
- data_ = v.data_; // Assign as POD.
-
- null = v.null;
- }
- else
- *this = nullptr;
- }
-
- return *this;
- }
-
- value& value::
- operator= (const value& v)
- {
- if (this != &v)
- {
- // Prepare the receiving value.
- //
- if (type != v.type)
- {
- *this = nullptr;
- type = v.type;
- }
-
- // Now our types are the same. If the receiving value is NULL, then call
- // copy_ctor() instead of copy_assign().
- //
- if (v)
- {
- if (type == nullptr)
- {
- if (null)
- new (&data_) names (v.as<names> ());
- else
- as<names> () = v.as<names> ();
- }
- else if (auto f = null ? type->copy_ctor : type->copy_assign)
- f (*this, v, false);
- else
- data_ = v.data_; // Assign as POD.
-
- null = v.null;
- }
- else
- *this = nullptr;
- }
-
- return *this;
- }
-
- void value::
- assign (names&& ns, const variable* var)
- {
- assert (type == nullptr || type->assign != nullptr);
-
- if (type == nullptr)
- {
- if (null)
- new (&data_) names (move (ns));
- else
- as<names> () = move (ns);
- }
- else
- type->assign (*this, move (ns), var);
-
- null = false;
- }
-
- void value::
- append (names&& ns, const variable* var)
- {
- if (type == nullptr)
- {
- if (null)
- new (&data_) names (move (ns));
- else
- {
- names& p (as<names> ());
-
- if (p.empty ())
- p = move (ns);
- else if (!ns.empty ())
- {
- p.insert (p.end (),
- make_move_iterator (ns.begin ()),
- make_move_iterator (ns.end ()));
- }
- }
- }
- else
- {
- if (type->append == nullptr)
- {
- diag_record dr (fail);
-
- dr << "cannot append to " << type->name << " value";
-
- if (var != nullptr)
- dr << " in variable " << var->name;
- }
-
- type->append (*this, move (ns), var);
- }
-
- null = false;
- }
-
- void value::
- prepend (names&& ns, const variable* var)
- {
- if (type == nullptr)
- {
- if (null)
- new (&data_) names (move (ns));
- else
- {
- names& p (as<names> ());
-
- if (p.empty ())
- p = move (ns);
- else if (!ns.empty ())
- {
- ns.insert (ns.end (),
- make_move_iterator (p.begin ()),
- make_move_iterator (p.end ()));
- p = move (ns);
- }
- }
- }
- else
- {
- if (type->prepend == nullptr)
- {
- diag_record dr (fail);
-
- dr << "cannot prepend to " << type->name << " value";
-
- if (var != nullptr)
- dr << " in variable " << var->name;
- }
-
- type->prepend (*this, move (ns), var);
- }
-
- null = false;
- }
-
- bool
- operator== (const value& x, const value& y)
- {
- bool xn (x.null);
- bool yn (y.null);
-
- assert (x.type == y.type ||
- (xn && x.type == nullptr) ||
- (yn && y.type == nullptr));
-
- if (xn || yn)
- return xn == yn;
-
- if (x.type == nullptr)
- return x.as<names> () == y.as<names> ();
-
- if (x.type->compare == nullptr)
- return memcmp (&x.data_, &y.data_, x.type->size) == 0;
-
- return x.type->compare (x, y) == 0;
- }
-
- bool
- operator< (const value& x, const value& y)
- {
- bool xn (x.null);
- bool yn (y.null);
-
- assert (x.type == y.type ||
- (xn && x.type == nullptr) ||
- (yn && y.type == nullptr));
-
- // NULL value is always less than non-NULL.
- //
- if (xn || yn)
- return xn > yn; // !xn < !yn
-
- if (x.type == nullptr)
- return x.as<names> () < y.as<names> ();
-
- if (x.type->compare == nullptr)
- return memcmp (&x.data_, &y.data_, x.type->size) < 0;
-
- return x.type->compare (x, y) < 0;
- }
-
- bool
- operator> (const value& x, const value& y)
- {
- bool xn (x.null);
- bool yn (y.null);
-
- assert (x.type == y.type ||
- (xn && x.type == nullptr) ||
- (yn && y.type == nullptr));
-
- // NULL value is always less than non-NULL.
- //
- if (xn || yn)
- return xn < yn; // !xn > !yn
-
- if (x.type == nullptr)
- return x.as<names> () > y.as<names> ();
-
- if (x.type->compare == nullptr)
- return memcmp (&x.data_, &y.data_, x.type->size) > 0;
-
- return x.type->compare (x, y) > 0;
- }
-
- void
- typify (value& v, const value_type& t, const variable* var, memory_order mo)
- {
- if (v.type == nullptr)
- {
- if (v)
- {
- // Note: the order in which we do things here is important.
- //
- names ns (move (v).as<names> ());
- v = nullptr;
-
- // Use value_type::assign directly to delay v.type change.
- //
- t.assign (v, move (ns), var);
- v.null = false;
- }
- else
- v.type = &t;
-
- v.type.store (&t, mo);
- }
- else if (v.type != &t)
- {
- diag_record dr (fail);
-
- dr << "type mismatch";
-
- if (var != nullptr)
- dr << " in variable " << var->name;
-
- dr << info << "value type is " << v.type->name;
- dr << info << (var != nullptr && &t == var->type ? "variable" : "new")
- << " type is " << t.name;
- }
- }
-
- void
- typify_atomic (value& v, const value_type& t, const variable* var)
- {
- // Typification is kind of like caching so we reuse that mutex shard.
- //
- shared_mutex& m (
- variable_cache_mutex_shard[
- hash<value*> () (&v) % variable_cache_mutex_shard_size]);
-
- // Note: v.type is rechecked by typify() under lock.
- //
- ulock l (m);
- typify (v, t, var, memory_order_release);
- }
-
- void
- untypify (value& v)
- {
- if (v.type == nullptr)
- return;
-
- if (v.null)
- {
- v.type = nullptr;
- return;
- }
-
- names ns;
- names_view nv (v.type->reverse (v, ns));
-
- if (nv.empty () || nv.data () == ns.data ())
- {
- // If the data is in storage, then we are all set.
- //
- ns.resize (nv.size ()); // Just to be sure.
- }
- else
- {
- // If the data is somewhere in the value itself, then steal it.
- //
- auto b (const_cast<name*> (nv.data ()));
- ns.assign (make_move_iterator (b),
- make_move_iterator (b + nv.size ()));
- }
-
- v = nullptr; // Free old data.
- v.type = nullptr; // Change type.
- v.assign (move (ns), nullptr); // Assign new data.
- }
-
- // Throw invalid_argument for an invalid simple value.
- //
- [[noreturn]] static void
- throw_invalid_argument (const name& n, const name* r, const char* type)
- {
- string m;
- string t (type);
-
- if (r != nullptr)
- m = "pair in " + t + " value";
- else
- {
- m = "invalid " + t + " value: ";
-
- if (n.simple ())
- m += "'" + n.value + "'";
- else if (n.directory ())
- m += "'" + n.dir.representation () + "'";
- else
- m += "complex name";
- }
-
- throw invalid_argument (m);
- }
-
- // names
- //
- const names& value_traits<names>::empty_instance = empty_names;
-
- // bool value
- //
- bool value_traits<bool>::
- convert (name&& n, name* r)
- {
- if (r == nullptr && n.simple ())
- {
- const string& s (n.value);
-
- if (s == "true")
- return true;
-
- if (s == "false")
- return false;
-
- // Fall through.
- }
-
- throw_invalid_argument (n, r, "bool");
- }
-
- const char* const value_traits<bool>::type_name = "bool";
-
- const value_type value_traits<bool>::value_type
- {
- type_name,
- sizeof (bool),
- nullptr, // No base.
- nullptr, // No element.
- nullptr, // No dtor (POD).
- nullptr, // No copy_ctor (POD).
- nullptr, // No copy_assign (POD).
- &simple_assign<bool>,
- &simple_append<bool>,
- &simple_append<bool>, // Prepend same as append.
- &simple_reverse<bool>,
- nullptr, // No cast (cast data_ directly).
- nullptr, // No compare (compare as POD).
- nullptr // Never empty.
- };
-
- // uint64_t value
- //
- uint64_t value_traits<uint64_t>::
- convert (name&& n, name* r)
- {
- if (r == nullptr && n.simple ())
- {
- try
- {
- // May throw invalid_argument or out_of_range.
- //
- return stoull (n.value);
- }
- catch (const std::exception&)
- {
- // Fall through.
- }
- }
-
- throw_invalid_argument (n, r, "uint64");
- }
-
- const char* const value_traits<uint64_t>::type_name = "uint64";
-
- const value_type value_traits<uint64_t>::value_type
- {
- type_name,
- sizeof (uint64_t),
- nullptr, // No base.
- nullptr, // No element.
- nullptr, // No dtor (POD).
- nullptr, // No copy_ctor (POD).
- nullptr, // No copy_assign (POD).
- &simple_assign<uint64_t>,
- &simple_append<uint64_t>,
- &simple_append<uint64_t>, // Prepend same as append.
- &simple_reverse<uint64_t>,
- nullptr, // No cast (cast data_ directly).
- nullptr, // No compare (compare as POD).
- nullptr // Never empty.
- };
-
- // string value
- //
- string value_traits<string>::
- convert (name&& n, name* r)
- {
- // The goal is to reverse the name into its original representation. The
- // code is a bit convoluted because we try to avoid extra allocations for
- // the common cases (unqualified, unpaired simple name or directory).
- //
-
- // We can only convert project-qualified simple and directory names.
- //
- if (!(n.simple (true) || n.directory (true)) ||
- !(r == nullptr || r->simple (true) || r->directory (true)))
- throw_invalid_argument (n, r, "string");
-
- string s;
-
- if (n.directory (true))
- // Note that here we cannot assume what's in dir is really a
- // path (think s/foo/bar/) so we have to reverse it exactly.
- //
- s = move (n.dir).representation (); // Move out of path.
- else
- s.swap (n.value);
-
- // Convert project qualification to its string representation.
- //
- if (n.qualified ())
- {
- string p (move (*n.proj).string ());
- p += '%';
- p += s;
- p.swap (s);
- }
-
- // The same for the RHS of a pair, if we have one.
- //
- if (r != nullptr)
- {
- s += '@';
-
- if (r->qualified ())
- {
- s += r->proj->string ();
- s += '%';
- }
-
- if (r->directory (true))
- s += move (r->dir).representation ();
- else
- s += r->value;
- }
-
- return s;
- }
-
- const string& value_traits<string>::empty_instance = empty_string;
-
- const char* const value_traits<string>::type_name = "string";
-
- const value_type value_traits<string>::value_type
- {
- type_name,
- sizeof (string),
- nullptr, // No base.
- nullptr, // No element.
- &default_dtor<string>,
- &default_copy_ctor<string>,
- &default_copy_assign<string>,
- &simple_assign<string>,
- &simple_append<string>,
- &simple_prepend<string>,
- &simple_reverse<string>,
- nullptr, // No cast (cast data_ directly).
- &simple_compare<string>,
- &default_empty<string>
- };
-
- // path value
- //
- path value_traits<path>::
- convert (name&& n, name* r)
- {
- if (r == nullptr)
- {
- // A directory path is a path.
- //
- if (n.directory ())
- return move (n.dir);
-
- if (n.simple ())
- {
- try
- {
- return path (move (n.value));
- }
- catch (invalid_path& e)
- {
- n.value = move (e.path); // Restore the name object for diagnostics.
- // Fall through.
- }
- }
-
- // Reassemble split dir/value.
- //
- if (n.untyped () && n.unqualified ())
- {
- try
- {
- return n.dir / n.value;
- }
- catch (const invalid_path&)
- {
- // Fall through.
- }
- }
-
- // Fall through.
- }
-
- throw_invalid_argument (n, r, "path");
- }
-
- const path& value_traits<path>::empty_instance = empty_path;
-
- const char* const value_traits<path>::type_name = "path";
-
- const value_type value_traits<path>::value_type
- {
- type_name,
- sizeof (path),
- nullptr, // No base.
- nullptr, // No element.
- &default_dtor<path>,
- &default_copy_ctor<path>,
- &default_copy_assign<path>,
- &simple_assign<path>,
- &simple_append<path>,
- &simple_prepend<path>,
- &simple_reverse<path>,
- nullptr, // No cast (cast data_ directly).
- &simple_compare<path>,
- &default_empty<path>
- };
-
- // dir_path value
- //
- dir_path value_traits<dir_path>::
- convert (name&& n, name* r)
- {
- if (r == nullptr)
- {
- if (n.directory ())
- return move (n.dir);
-
- if (n.simple ())
- {
- try
- {
- return dir_path (move (n.value));
- }
- catch (invalid_path& e)
- {
- n.value = move (e.path); // Restore the name object for diagnostics.
- // Fall through.
- }
- }
-
- // Reassemble split dir/value.
- //
- if (n.untyped () && n.unqualified ())
- {
- try
- {
- n.dir /= n.value;
- return move (n.dir);
- }
- catch (const invalid_path&)
- {
- // Fall through.
- }
- }
-
- // Fall through.
- }
-
- throw_invalid_argument (n, r, "dir_path");
- }
-
- const dir_path& value_traits<dir_path>::empty_instance = empty_dir_path;
-
- const char* const value_traits<dir_path>::type_name = "dir_path";
-
- const value_type value_traits<dir_path>::value_type
- {
- type_name,
- sizeof (dir_path),
- &value_traits<path>::value_type, // Base (assuming direct cast works for
- // both).
- nullptr, // No element.
- &default_dtor<dir_path>,
- &default_copy_ctor<dir_path>,
- &default_copy_assign<dir_path>,
- &simple_assign<dir_path>,
- &simple_append<dir_path>,
- &simple_prepend<dir_path>,
- &simple_reverse<dir_path>,
- nullptr, // No cast (cast data_ directly).
- &simple_compare<dir_path>,
- &default_empty<dir_path>
- };
-
- // abs_dir_path value
- //
- abs_dir_path value_traits<abs_dir_path>::
- convert (name&& n, name* r)
- {
- if (r == nullptr && (n.simple () || n.directory ()))
- {
- try
- {
- dir_path d (n.simple () ? dir_path (move (n.value)) : move (n.dir));
-
- if (!d.empty ())
- {
- if (d.relative ())
- d.complete ();
-
- d.normalize (true); // Actualize.
- }
-
- return abs_dir_path (move (d));
- }
- catch (const invalid_path&) {} // Fall through.
- }
-
- throw_invalid_argument (n, r, "abs_dir_path");
- }
-
- const char* const value_traits<abs_dir_path>::type_name = "abs_dir_path";
-
- const value_type value_traits<abs_dir_path>::value_type
- {
- type_name,
- sizeof (abs_dir_path),
- &value_traits<dir_path>::value_type, // Base (assuming direct cast works
- // for both).
- nullptr, // No element.
- &default_dtor<abs_dir_path>,
- &default_copy_ctor<abs_dir_path>,
- &default_copy_assign<abs_dir_path>,
- &simple_assign<abs_dir_path>,
- &simple_append<abs_dir_path>,
- nullptr, // No prepend.
- &simple_reverse<abs_dir_path>,
- nullptr, // No cast (cast data_ directly).
- &simple_compare<abs_dir_path>,
- &default_empty<abs_dir_path>
- };
-
- // name value
- //
- name value_traits<name>::
- convert (name&& n, name* r)
- {
- if (r == nullptr)
- return move (n);
-
- throw_invalid_argument (n, r, "name");
- }
-
- static names_view
- name_reverse (const value& v, names&)
- {
- const name& n (v.as<name> ());
- return n.empty () ? names_view (nullptr, 0) : names_view (&n, 1);
- }
-
- const char* const value_traits<name>::type_name = "name";
-
- const value_type value_traits<name>::value_type
- {
- type_name,
- sizeof (name),
- nullptr, // No base.
- nullptr, // No element.
- &default_dtor<name>,
- &default_copy_ctor<name>,
- &default_copy_assign<name>,
- &simple_assign<name>,
- nullptr, // Append not supported.
- nullptr, // Prepend not supported.
- &name_reverse,
- nullptr, // No cast (cast data_ directly).
- &simple_compare<name>,
- &default_empty<name>
- };
-
- // name_pair
- //
- name_pair value_traits<name_pair>::
- convert (name&& n, name* r)
- {
- n.pair = '\0'; // Keep "unpaired" in case r is empty.
- return name_pair (move (n), r != nullptr ? move (*r) : name ());
- }
-
- void
- name_pair_assign (value& v, names&& ns, const variable* var)
- {
- using traits = value_traits<name_pair>;
-
- size_t n (ns.size ());
-
- if (n <= 2)
- {
- try
- {
- traits::assign (
- v,
- (n == 0
- ? name_pair ()
- : traits::convert (move (ns[0]), n == 2 ? &ns[1] : nullptr)));
- return;
- }
- catch (const invalid_argument&) {} // Fall through.
- }
-
- diag_record dr (fail);
- dr << "invalid name_pair value '" << ns << "'";
-
- if (var != nullptr)
- dr << " in variable " << var->name;
- }
-
- static names_view
- name_pair_reverse (const value& v, names& ns)
- {
- const name_pair& p (v.as<name_pair> ());
- const name& f (p.first);
- const name& s (p.second);
-
- if (f.empty () && s.empty ())
- return names_view (nullptr, 0);
-
- if (f.empty ())
- return names_view (&s, 1);
-
- if (s.empty ())
- return names_view (&f, 1);
-
- ns.push_back (f);
- ns.back ().pair = '@';
- ns.push_back (s);
- return ns;
- }
-
- const char* const value_traits<name_pair>::type_name = "name_pair";
-
- const value_type value_traits<name_pair>::value_type
- {
- type_name,
- sizeof (name_pair),
- nullptr, // No base.
- nullptr, // No element.
- &default_dtor<name_pair>,
- &default_copy_ctor<name_pair>,
- &default_copy_assign<name_pair>,
- &name_pair_assign,
- nullptr, // Append not supported.
- nullptr, // Prepend not supported.
- &name_pair_reverse,
- nullptr, // No cast (cast data_ directly).
- &simple_compare<name_pair>,
- &default_empty<name_pair>
- };
-
- // process_path value
- //
- process_path value_traits<process_path>::
- convert (name&& n, name* r)
- {
- if ( n.untyped () && n.unqualified () && !n.empty () &&
- (r == nullptr || (r->untyped () && r->unqualified () && !r->empty ())))
- {
- path rp (move (n.dir));
- if (rp.empty ())
- rp = path (move (n.value));
- else
- rp /= n.value;
-
- path ep;
- if (r != nullptr)
- {
- ep = move (r->dir);
- if (ep.empty ())
- ep = path (move (r->value));
- else
- ep /= r->value;
- }
-
- process_path pp (nullptr, move (rp), move (ep));
- pp.initial = pp.recall.string ().c_str ();
- return pp;
- }
-
- throw_invalid_argument (n, r, "process_path");
- }
-
- void
- process_path_assign (value& v, names&& ns, const variable* var)
- {
- using traits = value_traits<process_path>;
-
- size_t n (ns.size ());
-
- if (n <= 2)
- {
- try
- {
- traits::assign (
- v,
- (n == 0
- ? process_path ()
- : traits::convert (move (ns[0]), n == 2 ? &ns[1] : nullptr)));
- return;
- }
- catch (const invalid_argument&) {} // Fall through.
- }
-
- diag_record dr (fail);
- dr << "invalid process_path value '" << ns << "'";
-
- if (var != nullptr)
- dr << " in variable " << var->name;
- }
-
- void
- process_path_copy_ctor (value& l, const value& r, bool m)
- {
- const auto& rhs (r.as<process_path> ());
-
- if (m)
- new (&l.data_) process_path (move (const_cast<process_path&> (rhs)));
- else
- {
- auto& lhs (
- *new (&l.data_) process_path (
- nullptr, path (rhs.recall), path (rhs.effect)));
- lhs.initial = lhs.recall.string ().c_str ();
- }
- }
-
- void
- process_path_copy_assign (value& l, const value& r, bool m)
- {
- auto& lhs (l.as<process_path> ());
- const auto& rhs (r.as<process_path> ());
-
- if (m)
- lhs = move (const_cast<process_path&> (rhs));
- else
- {
- lhs.recall = rhs.recall;
- lhs.effect = rhs.effect;
- lhs.initial = lhs.recall.string ().c_str ();
- }
- }
-
- static names_view
- process_path_reverse (const value& v, names& s)
- {
- const process_path& x (v.as<process_path> ());
-
- if (!x.empty ())
- {
- s.reserve (x.effect.empty () ? 1 : 2);
-
- s.push_back (name (x.recall.directory (),
- string (),
- x.recall.leaf ().string ()));
-
- if (!x.effect.empty ())
- {
- s.back ().pair = '@';
- s.push_back (name (x.effect.directory (),
- string (),
- x.effect.leaf ().string ()));
- }
- }
-
- return s;
- }
-
- const char* const value_traits<process_path>::type_name = "process_path";
-
- const value_type value_traits<process_path>::value_type
- {
- type_name,
- sizeof (process_path),
- nullptr, // No base.
- nullptr, // No element.
- &default_dtor<process_path>,
- &process_path_copy_ctor,
- &process_path_copy_assign,
- &process_path_assign,
- nullptr, // Append not supported.
- nullptr, // Prepend not supported.
- &process_path_reverse,
- nullptr, // No cast (cast data_ directly).
- &simple_compare<process_path>,
- &default_empty<process_path>
- };
-
- // target_triplet value
- //
- target_triplet value_traits<target_triplet>::
- convert (name&& n, name* r)
- {
- if (r == nullptr)
- {
- if (n.simple ())
- {
- try
- {
- return n.empty () ? target_triplet () : target_triplet (n.value);
- }
- catch (const invalid_argument& e)
- {
- throw invalid_argument (
- string ("invalid target_triplet value: ") + e.what ());
- }
- }
-
- // Fall through.
- }
-
- throw_invalid_argument (n, r, "target_triplet");
- }
-
- const char* const value_traits<target_triplet>::type_name = "target_triplet";
-
- const value_type value_traits<target_triplet>::value_type
- {
- type_name,
- sizeof (target_triplet),
- nullptr, // No base.
- nullptr, // No element.
- &default_dtor<target_triplet>,
- &default_copy_ctor<target_triplet>,
- &default_copy_assign<target_triplet>,
- &simple_assign<target_triplet>,
- nullptr, // Append not supported.
- nullptr, // Prepend not supported.
- &simple_reverse<target_triplet>,
- nullptr, // No cast (cast data_ directly).
- &simple_compare<target_triplet>,
- &default_empty<target_triplet>
- };
-
- // project_name value
- //
- project_name value_traits<project_name>::
- convert (name&& n, name* r)
- {
- if (r == nullptr)
- {
- if (n.simple ())
- {
- try
- {
- return n.empty () ? project_name () : project_name (move (n.value));
- }
- catch (const invalid_argument& e)
- {
- throw invalid_argument (
- string ("invalid project_name value: ") + e.what ());
- }
- }
-
- // Fall through.
- }
-
- throw_invalid_argument (n, r, "project_name");
- }
-
- const project_name&
- value_traits<project_name>::empty_instance = empty_project_name;
-
- const char* const value_traits<project_name>::type_name = "project_name";
-
- const value_type value_traits<project_name>::value_type
- {
- type_name,
- sizeof (project_name),
- nullptr, // No base.
- nullptr, // No element.
- &default_dtor<project_name>,
- &default_copy_ctor<project_name>,
- &default_copy_assign<project_name>,
- &simple_assign<project_name>,
- nullptr, // Append not supported.
- nullptr, // Prepend not supported.
- &simple_reverse<project_name>,
- nullptr, // No cast (cast data_ directly).
- &simple_compare<project_name>,
- &default_empty<project_name>
- };
-
- // variable_pool
- //
- void variable_pool::
- update (variable& var,
- const build2::value_type* t,
- const variable_visibility* v,
- const bool* o) const
- {
- // Check overridability (all overrides, if any, should already have
- // been entered (see context.cxx:reset()).
- //
- if (var.overrides != nullptr && (o == nullptr || !*o))
- fail << "variable " << var.name << " cannot be overridden";
-
- bool ut (t != nullptr && var.type != t);
- bool uv (v != nullptr && var.visibility != *v);
-
- // Variable should not be updated post-aliasing.
- //
- assert (var.aliases == &var || (!ut && !uv));
-
- // Update type?
- //
- if (ut)
- {
- assert (var.type == nullptr);
- var.type = t;
- }
-
- // Change visibility? While this might at first seem like a bad idea,
- // it can happen that the variable lookup happens before any values
- // were set, in which case the variable will be entered with the
- // default visibility.
- //
- if (uv)
- {
- assert (var.visibility == variable_visibility::normal); // Default.
- var.visibility = *v;
- }
- }
-
- static bool
- match_pattern (const string& n, const string& p, const string& s, bool multi)
- {
- size_t nn (n.size ()), pn (p.size ()), sn (s.size ());
-
- if (nn < pn + sn + 1)
- return false;
-
- if (pn != 0)
- {
- if (n.compare (0, pn, p) != 0)
- return false;
- }
-
- if (sn != 0)
- {
- if (n.compare (nn - sn, sn, s) != 0)
- return false;
- }
-
- // Make sure the stem is a single name unless instructed otherwise.
- //
- return multi || string::traits_type::find (n.c_str () + pn,
- nn - pn - sn,
- '.') == nullptr;
- }
-
- static inline void
- merge_pattern (const variable_pool::pattern& p,
- const build2::value_type*& t,
- const variable_visibility*& v,
- const bool*& o)
- {
- if (p.type)
- {
- if (t == nullptr)
- t = *p.type;
- else if (p.match)
- assert (t == *p.type);
- }
-
- if (p.visibility)
- {
- if (v == nullptr)
- v = &*p.visibility;
- else if (p.match)
- assert (*v == *p.visibility);
- }
-
- if (p.overridable)
- {
- if (o == nullptr)
- o = &*p.overridable;
- else if (p.match)
- {
- // Allow the pattern to restrict but not relax.
- //
- if (*o)
- o = &*p.overridable;
- else
- assert (*o == *p.overridable);
- }
- }
- }
-
- variable& variable_pool::
- insert (string n,
- const build2::value_type* t,
- const variable_visibility* v,
- const bool* o,
- bool pat)
- {
- assert (!global_ || phase == run_phase::load);
-
- // Apply pattern.
- //
- if (pat)
- {
- if (n.find ('.') != string::npos)
- {
- // Reverse means from the "largest" (most specific).
- //
- for (const pattern& p: reverse_iterate (patterns_))
- {
- if (match_pattern (n, p.prefix, p.suffix, p.multi))
- {
- merge_pattern (p, t, v, o);
- break;
- }
- }
- }
- }
-
- auto p (
- insert (
- variable {
- move (n),
- nullptr,
- t,
- nullptr,
- v != nullptr ? *v : variable_visibility::normal}));
-
- variable& r (p.first->second);
-
- if (p.second)
- r.aliases = &r;
- else // Note: overridden variable will always exist.
- {
- if (t != nullptr || v != nullptr || o != nullptr)
- update (r, t, v, o); // Not changing the key.
- else if (r.overrides != nullptr)
- fail << "variable " << r.name << " cannot be overridden";
- }
-
- return r;
- }
-
- const variable& variable_pool::
- insert_alias (const variable& var, string n)
- {
- assert (var.aliases != nullptr && var.overrides == nullptr);
-
- variable& a (insert (move (n),
- var.type,
- &var.visibility,
- nullptr /* override */,
- false /* pattern */));
-
- if (a.aliases == &a) // Not aliased yet.
- {
- a.aliases = var.aliases;
- const_cast<variable&> (var).aliases = &a;
- }
- else
- assert (a.alias (var)); // Make sure it is already an alias of var.
-
- return a;
- }
-
- void variable_pool::
- insert_pattern (const string& p,
- optional<const value_type*> t,
- optional<bool> o,
- optional<variable_visibility> v,
- bool retro,
- bool match)
- {
- assert (!global_ || phase == run_phase::load);
-
- size_t pn (p.size ());
-
- size_t w (p.find ('*'));
- assert (w != string::npos);
-
- bool multi (w + 1 != pn && p[w + 1] == '*');
-
- // Extract prefix and suffix.
- //
- string pfx, sfx;
-
- if (w != 0)
- {
- assert (p[w - 1] == '.' && w != 1);
- pfx.assign (p, 0, w);
- }
-
- w += multi ? 2 : 1; // First suffix character.
- size_t sn (pn - w); // Suffix length.
-
- if (sn != 0)
- {
- assert (p[w] == '.' && sn != 1);
- sfx.assign (p, w, sn);
- }
-
- auto i (
- patterns_.insert (
- pattern {move (pfx), move (sfx), multi, match, t, v, o}));
-
- // Apply retrospectively to existing variables.
- //
- if (retro)
- {
- for (auto& p: map_)
- {
- variable& var (p.second);
-
- if (match_pattern (var.name, i->prefix, i->suffix, i->multi))
- {
- // Make sure that none of the existing more specific patterns
- // match.
- //
- auto j (i), e (patterns_.end ());
- for (++j; j != e; ++j)
- {
- if (match_pattern (var.name, j->prefix, j->suffix, j->multi))
- break;
- }
-
- if (j == e)
- update (var,
- t ? *t : nullptr,
- v ? &*v : nullptr,
- o ? &*o : nullptr); // Not changing the key.
- }
- }
- }
- }
-
- variable_pool variable_pool::instance (true);
- const variable_pool& variable_pool::cinstance = variable_pool::instance;
- const variable_pool& var_pool = variable_pool::cinstance;
-
- // variable_map
- //
- auto variable_map::
- find (const variable& var, bool typed) const ->
- pair<const value_data*, const variable&>
- {
- const variable* v (&var);
- const value_data* r (nullptr);
- do
- {
- // @@ Should we verify that there are no distinct values for aliases?
- // This can happen if the values were entered before the variables
- // were aliased. Possible but probably highly unlikely.
- //
- auto i (m_.find (*v));
- if (i != m_.end ())
- {
- r = &i->second;
- break;
- }
-
- v = v->aliases;
-
- } while (v != &var && v != nullptr);
-
- // Check if this is the first access after being assigned a type.
- //
- if (r != nullptr && typed && v->type != nullptr)
- typify (*r, *v);
-
- return pair<const value_data*, const variable&> (
- r, r != nullptr ? *v : var);
- }
-
- auto variable_map::
- find_to_modify (const variable& var, bool typed) ->
- pair<value_data*, const variable&>
- {
- auto p (find (var, typed));
- auto* r (const_cast<value_data*> (p.first));
-
- if (r != nullptr)
- r->version++;
-
- return pair<value_data*, const variable&> (r, p.second);
- }
-
- pair<reference_wrapper<value>, bool> variable_map::
- insert (const variable& var, bool typed)
- {
- assert (!global_ || phase == run_phase::load);
-
- auto p (m_.emplace (var, value_data (typed ? var.type : nullptr)));
- value_data& r (p.first->second);
-
- if (!p.second)
- {
- // Check if this is the first access after being assigned a type.
- //
- // Note: we still need atomic in case this is not a global state.
- //
- if (typed && var.type != nullptr)
- typify (r, var);
- }
-
- r.version++;
-
- return make_pair (reference_wrapper<value> (r), p.second);
- }
-
- // variable_type_map
- //
- lookup variable_type_map::
- find (const target_type& type,
- const string& name,
- const variable& var) const
- {
- // Search across target type hierarchy.
- //
- for (auto tt (&type); tt != nullptr; tt = tt->base)
- {
- auto i (map_.find (*tt));
-
- if (i == end ())
- continue;
-
- // Try to match the pattern, starting from the longest values
- // so that the more "specific" patterns (i.e., those that cover
- // fewer characters with the wildcard) take precedence. See
- // tests/variable/type-pattern.
- //
- const variable_pattern_map& m (i->second);
-
- for (auto j (m.rbegin ()); j != m.rend (); ++j)
- {
- const string& pat (j->first);
-
- //@@ TODO: should we detect ambiguity? 'foo-*' '*-foo' and 'foo-foo'?
- // Right now the last defined will be used.
- //
- if (pat != "*")
- {
- if (name.size () < pat.size () - 1 || // One for '*' or '?'.
- !butl::path_match (pat, name))
- continue;
- }
-
- // Ok, this pattern matches. But is there a variable?
- //
- // Since we store append/prepend values untyped, instruct find() not
- // to automatically type it. And if it is assignment, then typify it
- // ourselves.
- //
- const variable_map& vm (j->second);
- {
- auto p (vm.find (var, false));
- if (const variable_map::value_data* v = p.first)
- {
- // Check if this is the first access after being assigned a type.
- //
- if (v->extra == 0 && var.type != nullptr)
- vm.typify (*v, var);
-
- return lookup (*v, p.second, vm);
- }
- }
- }
- }
-
- return lookup ();
- }
-
- size_t variable_cache_mutex_shard_size;
- unique_ptr<shared_mutex[]> variable_cache_mutex_shard;
-}