aboutsummaryrefslogtreecommitdiff
path: root/build
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-03-04 16:33:51 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-03-04 16:33:51 +0200
commit7eed858cac7e8ff78626bdc5d63a7f36ca8f8010 (patch)
tree225dd25e354c4f6234dbf2c02608ec6545dcd688 /build
parentc76fe316122969986103d243706dc7fa7ab6ddc1 (diff)
Move roots and bases to appropriate scopes
Diffstat (limited to 'build')
-rw-r--r--build/b.cxx56
-rw-r--r--build/context22
-rw-r--r--build/context.cxx42
-rw-r--r--build/cxx/rule.cxx29
-rw-r--r--build/diagnostics.cxx25
-rw-r--r--build/name4
-rw-r--r--build/name.cxx2
-rw-r--r--build/parser3
-rw-r--r--build/parser.cxx102
-rw-r--r--build/path6
-rw-r--r--build/path.ixx13
-rw-r--r--build/scope10
-rw-r--r--build/scope.cxx54
-rw-r--r--build/target.cxx2
-rw-r--r--build/utility9
-rw-r--r--build/utility.cxx3
-rw-r--r--build/variable106
-rw-r--r--build/variable.cxx47
18 files changed, 424 insertions, 111 deletions
diff --git a/build/b.cxx b/build/b.cxx
index edefbe9..5509cba 100644
--- a/build/b.cxx
+++ b/build/b.cxx
@@ -98,7 +98,7 @@ main (int argc, char* argv[])
target_types.insert (cxx::ixx::static_type);
target_types.insert (cxx::txx::static_type);
- // Figure out directories: work, home, and {src,out}_{root,base}.
+ // Figure out work and home directories.
//
work = path::current ();
@@ -117,24 +117,48 @@ main (int argc, char* argv[])
home = path (pw->pw_dir);
}
+ if (verb >= 4)
+ {
+ trace << "work dir: " << work.string ();
+ trace << "home dir: " << home.string ();
+ }
+
+ // Create root scope. For Win32 we use the empty path since there
+ // is no such "real" root path. On POSIX, however, this is a real
+ // path. See the comment in <build/path-map> for details.
+ //
+#ifdef _WIN32
+ root_scope = &scopes[path ()];
+#else
+ root_scope = &scopes[path ("/")];
+#endif
+
+ // Figure out {src,out}_{root,base}. Note that all the paths must be
+ // normalized.
+ //
//@@ Must be normalized.
//
- out_base = work;
- src_base = out_base;
+ path out_base (work);
+ path src_base (out_base); //@@ TMP
+
+ path src_root;
+ path out_root;
// The project's root directory is the one that contains the build/
// sub-directory which contains the pre.build file.
//
for (path d (src_base); !d.root () && d != home; d = d.directory ())
{
- path f (d / path ("build/pre.build"));
- if (path_mtime (f) != timestamp_nonexistent)
+ if (path_mtime (d / path ("build/pre.build")) != timestamp_nonexistent)
{
src_root = d;
break;
}
}
+ // If there is no such sub-directory, assume this is a simple project
+ // with src_root being the same as src_base.
+ //
if (src_root.empty ())
{
src_root = src_base;
@@ -145,23 +169,23 @@ main (int argc, char* argv[])
if (verb >= 4)
{
- trace << "work dir: " << work.string ();
- trace << "home dir: " << home.string ();
trace << "out_base: " << out_base.string ();
trace << "src_base: " << src_base.string ();
trace << "out_root: " << out_root.string ();
trace << "src_root: " << src_root.string ();
}
- // Create root scope. For Win32 we use the empty path since there
- // is no such "real" root path. On POSIX, however, this is a real
- // path. See the comment in <build/path-map> for details.
+ // Create project root and base scopes, set the corresponding
+ // variables.
//
-#ifdef _WIN32
- root_scope = &scopes[path ()];
-#else
- root_scope = &scopes[path ("/")];
-#endif
+ scope& proot_scope (scopes[out_root]);
+ scope& pbase_scope (scopes[out_base]);
+
+ proot_scope.variables["out_root"] = move (out_root);
+ proot_scope.variables["src_root"] = move (src_root);
+
+ pbase_scope.variables["out_base"] = out_base;
+ pbase_scope.variables["src_base"] = src_base;
// Parse buildfile.
//
@@ -176,7 +200,7 @@ main (int argc, char* argv[])
try
{
- p.parse (ifs, bf, scopes[out_base]);
+ p.parse (ifs, bf, pbase_scope);
}
catch (const std::ios_base::failure&)
{
diff --git a/build/context b/build/context
index c177603..7c7421d 100644
--- a/build/context
+++ b/build/context
@@ -12,26 +12,28 @@
namespace build
{
+ class scope;
+
extern path work;
extern path home;
- extern path src_root;
- extern path out_root;
-
- extern path src_base;
- extern path out_base;
-
// Return the src/out directory corresponding to the given out/src. The
// passed directory should be a sub-directory of out/src_root.
//
path
- src_out (const path&);
+ src_out (const path& out, scope&);
+
+ path
+ src_out (const path& out, const path& out_root, const path& src_root);
+
+ path
+ out_src (const path& src, scope&);
path
- out_src (const path&);
+ out_src (const path& src, const path& out_root, const path& src_root);
- // If possible, translate an absolute, normalized path into relative to
- // the work directory.
+ // If possible and beneficial, translate an absolute, normalized path
+ // into relative to the work directory.
//
path
relative_work (const path&);
diff --git a/build/context.cxx b/build/context.cxx
index f7c9650..6c37e38 100644
--- a/build/context.cxx
+++ b/build/context.cxx
@@ -7,6 +7,8 @@
#include <ostream>
#include <cassert>
+#include <build/scope>
+
using namespace std;
namespace build
@@ -14,21 +16,31 @@ namespace build
path work;
path home;
- path src_root;
- path out_root;
+ path
+ src_out (const path& out, scope& s)
+ {
+ return src_out (out,
+ s["out_root"].as<const path&> (),
+ s["src_root"].as<const path&> ());
+ }
- path src_base;
- path out_base;
+ path
+ out_src (const path& src, scope& s)
+ {
+ return out_src (src,
+ s["out_root"].as<const path&> (),
+ s["src_root"].as<const path&> ());
+ }
path
- src_out (const path& o)
+ src_out (const path& o, const path& out_root, const path& src_root)
{
assert (o.sub (out_root));
return src_root / o.leaf (out_root);
}
path
- out_src (const path& s)
+ out_src (const path& s, const path& out_root, const path& src_root)
{
assert (s.sub (src_root));
return out_root / s.leaf (src_root);
@@ -41,11 +53,25 @@ namespace build
return p.leaf (work);
// If work is a sub-path of {src,out}_root and this path is also a
- // sub-bath of it, then use '..' to form a relative path.
+ // sub-path of it, then use '..' to form a relative path.
+ //
+ // Don't think this is a good heuristic. For example, why shouldn't
+ // we display paths from imported projects as relative if they are
+ // more readable than absolute?
//
+ /*
if ((work.sub (src_root) && p.sub (src_root)) ||
- (work.sub (out_root) && p.sub (out_root))) // @@ cache
+ (work.sub (out_root) && p.sub (out_root)))
return p.relative (work);
+ */
+
+ if (p.root_directory () == work.root_directory ())
+ {
+ path r (p.relative (work));
+
+ if (r.string ().size () < p.string ().size ())
+ return r;
+ }
return p;
}
diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx
index eb61bd1..e495c27 100644
--- a/build/cxx/rule.cxx
+++ b/build/cxx/rule.cxx
@@ -136,7 +136,7 @@ namespace build
const char* args[] = {
"g++-4.9",
"-std=c++14",
- "-I", src_root.string ().c_str (),
+ "-I", ds["src_root"].as<const path&> ().string ().c_str (),
"-MM", //@@ -M
"-MG", // Treat missing headers as generated.
"-MQ", "*", // Quoted target (older version can't handle empty name).
@@ -269,7 +269,7 @@ namespace build
"g++-4.9",
"-std=c++14",
"-g",
- "-I", src_root.string ().c_str (),
+ "-I", o.prerequisites[0].get ().scope["src_root"].as<const path&> ().string ().c_str (),
"-c",
"-o", ro.string ().c_str (),
rs.string ().c_str (),
@@ -382,7 +382,13 @@ namespace build
if (e.path ().empty ())
e.path (e.dir / path (e.name));
- // Process prerequisited: do rule chaining for C and C++ source
+ // We may need the project roots for rule chaining (see below).
+ // We will resolve them lazily only if needed.
+ //
+ const path* out_root (nullptr);
+ const path* src_root (nullptr);
+
+ // Process prerequisites: do rule chaining for C and C++ source
// files as well as search and match.
//
for (auto& pr: t.prerequisites)
@@ -398,6 +404,17 @@ namespace build
continue;
}
+ if (out_root == nullptr)
+ {
+ // Which scope shall we use to resolve the roots? Unlikely,
+ // but possible, the prerequisite is from a different project
+ // altogether. So we are going to use the target's project.
+ //
+ scope& s (scopes.find (e.dir));
+ out_root = &s["out_root"].as<const path&> ();
+ src_root = &s["src_root"].as<const path&> ();
+ }
+
prerequisite& cp (p);
// Come up with the obj{} prerequisite. The c(xx){} prerequisite
@@ -408,15 +425,15 @@ namespace build
// possible it is under out_root (e.g., generated source).
//
path d;
- if (cp.dir.relative () || cp.dir.sub (out_root))
+ if (cp.dir.relative () || cp.dir.sub (*out_root))
d = cp.dir;
else
{
- if (!cp.dir.sub (src_root))
+ if (!cp.dir.sub (*src_root))
fail << "out of project prerequisite " << cp <<
info << "specify corresponding obj{} target explicitly";
- d = out_root / cp.dir.leaf (src_root);
+ d = *out_root / cp.dir.leaf (*src_root);
}
prerequisite& op (
diff --git a/build/diagnostics.cxx b/build/diagnostics.cxx
index 34bbee8..f5802de 100644
--- a/build/diagnostics.cxx
+++ b/build/diagnostics.cxx
@@ -21,14 +21,31 @@ namespace build
if (p == work)
return ".";
- path rp (relative_work (p));
+#ifndef _WIN32
+ if (p == home)
+ return "~";
+#endif
+
+ path rw (relative_work (p));
#ifndef _WIN32
- if (rp.absolute () && rp.sub (home))
- return "~/" + rp.leaf (home).string ();
+ if (rw.relative ())
+ {
+ // See if the original path with the ~/ shortcut is better
+ // that the relative to work.
+ //
+ if (p.sub (home))
+ {
+ path rh (p.leaf (home));
+ if (rw.string ().size () > rh.string ().size () + 2) // 2 for '~/'
+ return "~/" + rh.string ();
+ }
+ }
+ else if (rw.sub (home))
+ return "~/" + rw.leaf (home).string ();
#endif
- return rp.string ();
+ return rw.string ();
}
return p.string ();
diff --git a/build/name b/build/name
index db7b61c..00f0c00 100644
--- a/build/name
+++ b/build/name
@@ -17,12 +17,16 @@ namespace build
// A name is what we operate on by default. Depending on the context,
// it can be interpreted as a target or prerequisite name. A name
// without a type and directory can be used to represent any text.
+ // A name with directory and empty value represents a directory.
//
struct name
{
explicit
name (std::string v): value (std::move (v)) {}
+ explicit
+ name (path d): dir (std::move (d)) {}
+
name (std::string t, path d, std::string v)
: type (std::move (t)), dir (std::move (d)), value (std::move (v)) {}
diff --git a/build/name.cxx b/build/name.cxx
index 9de1695..4ffc848 100644
--- a/build/name.cxx
+++ b/build/name.cxx
@@ -30,6 +30,8 @@ namespace build
s.back () != path::traits::directory_separator)
os << path::traits::directory_separator;
}
+ else if (n.value.empty () && n.type.empty ())
+ os << s; // Otherwise nothing gets printed.
}
os << n.value;
diff --git a/build/parser b/build/parser
index e5aeb60..6e0be34 100644
--- a/build/parser
+++ b/build/parser
@@ -92,6 +92,9 @@ namespace build
scope* scope_;
target* default_target_;
+ const path* out_root_;
+ const path* src_root_;
+
token peek_ {token_type::eos, false, 0, 0};
bool peeked_ {false};
diff --git a/build/parser.cxx b/build/parser.cxx
index 1ff841b..a030a09 100644
--- a/build/parser.cxx
+++ b/build/parser.cxx
@@ -125,6 +125,9 @@ namespace build
scope_ = &s;
default_target_ = nullptr;
+ out_root_ = &s["out_root"].as<const path&> ();
+ src_root_ = &s["src_root"].as<const path&> ();
+
token t (type::eos, false, 0, 0);
type tt;
next (t, tt);
@@ -394,28 +397,28 @@ namespace build
}
else
{
- //@@ TODO: assuming it is a list.
- //
- list_value* val (dynamic_cast<list_value*> ((*scope_)[var]));
-
- if (val == nullptr) // Initialization.
+ if (auto val = (*scope_)[var])
{
- list_value_ptr nval (new list_value (*scope_, move (vns)));
- scope_->variables.emplace (var, move (nval));
+ //@@ TODO: assuming it is a list.
+ //
+ list_value* lv (&val.as<list_value&> ());
+
+ if (&lv->scope != scope_) // Append to value from parent scope?
+ {
+ list_value_ptr nval (new list_value (*scope_, lv->data));
+ lv = nval.get (); // Append to.
+ scope_->variables.emplace (var, move (nval));
+ }
+
+ lv->data.insert (lv->data.end (),
+ make_move_iterator (vns.begin ()),
+ make_move_iterator (vns.end ()));
}
- else if (&val->scope != scope_) // Append to value from parent scope.
+ else // Initialization.
{
- list_value_ptr nval (new list_value (*scope_, val->data));
- val = nval.get (); // Append.
+ list_value_ptr nval (new list_value (*scope_, move (vns)));
scope_->variables.emplace (var, move (nval));
}
-
- // Append.
- //
- if (val != nullptr)
- val->data.insert (val->data.end (),
- make_move_iterator (vns.begin ()),
- make_move_iterator (vns.end ()));
}
if (tt == type::newline)
@@ -454,7 +457,7 @@ namespace build
// to the current directory scope.
//
if (p.relative ())
- p = src_out (scope_->path ()) / p;
+ p = src_out (scope_->path (), *out_root_, *src_root_) / p;
ifstream ifs (p.string ());
@@ -532,14 +535,14 @@ namespace build
// Make sure the path is in this project. Include is only meant
// to be used for intra-project inclusion.
//
- if (!p.sub (src_root) && !(in_out = p.sub (out_root)))
+ if (!p.sub (*src_root_) && !(in_out = p.sub (*out_root_)))
fail (l) << "out of project include " << p;
}
else
{
// Use the src directory corresponding to the current directory scope.
//
- p = src_out (scope_->path ()) / p;
+ p = src_out (scope_->path (), *out_root_, *src_root_) / p;
p.normalize ();
}
@@ -549,6 +552,22 @@ namespace build
continue;
}
+ // Determine new bases.
+ //
+ path out_base;
+ path src_base;
+
+ if (in_out)
+ {
+ out_base = p.directory ();
+ src_base = src_out (out_base, *out_root_, *src_root_);
+ }
+ else
+ {
+ src_base = p.directory ();
+ out_base = out_src (src_base, *out_root_, *src_root_);
+ }
+
ifstream ifs (p.string ());
if (!ifs.is_open ())
@@ -567,7 +586,10 @@ namespace build
lexer_ = &l;
scope* os (scope_);
- scope_ = &scopes[(in_out ? p : out_src (p)).directory ()];
+ scope_ = &scopes[out_base];
+
+ scope_->variables["out_base"] = move (out_base);
+ scope_->variables["src_base"] = move (src_base);
target* odt (default_target_);
default_target_ = nullptr;
@@ -745,14 +767,12 @@ namespace build
//@@ TODO: append namespace if any.
n = t.name ();
- //@@ TODO: assuming it is a list.
- //
const variable& var (variable_pool.find (move (n)));
- list_value* val (dynamic_cast<list_value*> ((*scope_)[var]));
+ auto val ((*scope_)[var]);
// Undefined namespaces variables are not allowed.
//
- if (val == nullptr && var.name.find ('.') != string::npos)
+ if (!val && var.name.find ('.') != string::npos)
fail (t) << "undefined namespace variable " << var.name;
if (paren)
@@ -765,7 +785,14 @@ namespace build
tt = peek ();
- if (val == nullptr || val->data.empty ())
+ if (!val)
+ continue;
+
+ //@@ TODO: assuming it is a list.
+ //
+ const list_value& lv (val.as<list_value&> ());
+
+ if (lv.data.empty ())
continue;
// Should we accumulate? If the buffer is not empty, then
@@ -778,33 +805,36 @@ namespace build
((tt == type::name || tt == type::dollar) // Start.
&& !peeked ().separated ()))
{
- // This should be a simple value. The token still points
- // to the name (or closing paren).
+ // This should be a simple value or a simple directory. The
+ // token still points to the name (or closing paren).
//
- if (val->data.size () > 1)
+ if (lv.data.size () > 1)
fail (t) << "concatenating expansion of " << var.name
<< " contains multiple values";
- const name& n (val->data[0]);
+ const name& n (lv.data[0]);
if (!n.type.empty ())
fail (t) << "concatenating expansion of " << var.name
<< " contains type";
if (!n.dir.empty ())
- fail (t) << "concatenating expansion of " << var.name
- << " contains directory";
-
- concat += n.value;
+ {
+ if (!n.value.empty ())
+ fail (t) << "concatenating expansion of " << var.name
+ << " contains directory";
- text << "concat: " << concat;
+ concat += n.dir.string ();
+ }
+ else
+ concat += n.value;
}
else
{
// Copy the names from the variable into the resulting name list
// while doing sensible things with the types and directories.
//
- for (const name& n: val->data)
+ for (const name& n: lv.data)
{
const path* dp1 (dp);
const string* tp1 (tp);
diff --git a/build/path b/build/path
index 6ce2172..9b68485 100644
--- a/build/path
+++ b/build/path
@@ -287,6 +287,12 @@ namespace build
basic_path
directory (basic_path const&) const;
+ // Return the root directory of the path or empty path if
+ // the directory is not absolute.
+ //
+ basic_path
+ root_directory () const;
+
// Return the path without the extension, if any.
//
basic_path
diff --git a/build/path.ixx b/build/path.ixx
index 60d3bc3..ef1232b 100644
--- a/build/path.ixx
+++ b/build/path.ixx
@@ -59,6 +59,19 @@ namespace build
template <typename C>
inline basic_path<C> basic_path<C>::
+ root_directory () const
+ {
+ return absolute ()
+#ifdef _WIN32
+ ? path (path_, 2)
+#else
+ ? path ("/")
+#endif
+ : path ();
+ }
+
+ template <typename C>
+ inline basic_path<C> basic_path<C>::
base () const
{
size_type p (traits::find_extension (path_));
diff --git a/build/scope b/build/scope
index 2746ff8..b4f492d 100644
--- a/build/scope
+++ b/build/scope
@@ -26,18 +26,18 @@ namespace build
// Variable lookup.
//
public:
- value*
- operator[] (const variable&);
+ value_proxy
+ operator[] (const std::string&);
- value*
- operator[] (const std::string& name);
+ value_proxy
+ operator[] (const variable&);
private:
friend class scope_map;
typedef path_map<scope>::const_iterator iterator;
- scope () = default;
+ scope (): variables (*this) {}
void
init (const iterator& i, scope* p) {i_ = i; parent_ = p;}
diff --git a/build/scope.cxx b/build/scope.cxx
index 9013e12..b9b576e 100644
--- a/build/scope.cxx
+++ b/build/scope.cxx
@@ -10,24 +10,24 @@ namespace build
{
// scope
//
- value* scope::
+ value_proxy scope::
operator[] (const string& name)
{
const variable& var (variable_pool.find (name));
- return (*this)[var];
+ return operator[] (var);
}
- value* scope::
+ value_proxy scope::
operator[] (const variable& var)
{
for (scope* s (this); s != nullptr; s = s->parent ())
{
auto i (s->variables.find (var));
if (i != s->variables.end ())
- return i->second.get ();
+ return value_proxy (&i->second, s);
}
- return nullptr;
+ return value_proxy (nullptr, nullptr);
}
// scope_map
@@ -45,31 +45,37 @@ namespace build
{
scope* p (nullptr);
- // Update scopes of which we are a new parent.
+ // Update scopes of which we are a new parent (unless this is the
+ // root scope).
//
- for (auto r (find_prefix (k)); r.first != r.second; ++r.first)
+ if (size () > 1)
{
- scope& c (r.first->second);
-
- // The first scope of which we are a parent is the least
- // (shortest) one which means there is no other scope
- // between it and our parent.
+ // The first entry is ourselves.
+ //
+ auto r (find_prefix (k));
+ for (++r.first; r.first != r.second; ++r.first)
+ {
+ scope& c (r.first->second);
+
+ // The first scope of which we are a parent is the least
+ // (shortest) one which means there is no other scope
+ // between it and our parent.
+ //
+ if (p == nullptr)
+ p = c.parent ();
+ else if (p != c.parent ()) // A scope with an intermediate parent.
+ continue;
+
+ c.parent (s);
+ }
+
+ // We couldn't get the parent from one of its old children
+ // so we have to find it ourselves.
//
if (p == nullptr)
- p = c.parent ();
- else if (p != c.parent ()) // A scope with an intermediate parent.
- continue;
-
- c.parent (s);
+ p = &find (k.directory ());
}
- // We couldn't get the parent from one of its old children
- // so we have to find it ourselves (unless this is is the
- // root scope).
- //
- if (p == nullptr && size () != 1)
- p = &find (k.directory ());
-
s.init (er.first, p);
}
diff --git a/build/target.cxx b/build/target.cxx
index 136b912..6fdfb7b 100644
--- a/build/target.cxx
+++ b/build/target.cxx
@@ -141,7 +141,7 @@ namespace build
if (p.dir.relative ())
{
paths sp;
- sp.push_back (src_out (p.scope.path ())); // src_base
+ sp.push_back (src_out (p.scope.path (), p.scope)); // src_base
return search_existing_file (p, sp);
}
diff --git a/build/utility b/build/utility
index f429598..fd7888c 100644
--- a/build/utility
+++ b/build/utility
@@ -12,8 +12,17 @@
#include <exception>
#include <unordered_set>
+#include <build/path>
+
namespace build
{
+ // Empty string and path.
+ //
+ extern const std::string empty_string;
+ extern const path empty_path;
+
+ // Comparators.
+ //
struct compare_c_string
{
bool operator() (const char* x, const char* y) const
diff --git a/build/utility.cxx b/build/utility.cxx
index cd92740..c66fba2 100644
--- a/build/utility.cxx
+++ b/build/utility.cxx
@@ -8,6 +8,9 @@ using namespace std;
namespace build
{
+ const string empty_string;
+ const path empty_path;
+
bool exception_unwinding_dtor = false;
string_pool extension_pool;
diff --git a/build/variable b/build/variable
index 393ad47..ae4c7b3 100644
--- a/build/variable
+++ b/build/variable
@@ -8,9 +8,11 @@
#include <string>
#include <memory> // unique_ptr
#include <utility> // move()
+#include <cassert>
#include <typeindex>
#include <unordered_set>
+#include <build/path>
#include <build/name>
#include <build/prefix-map>
@@ -62,9 +64,90 @@ namespace build
{
list_value (scope_type& s, names d): value (s), data (std::move (d)) {}
+ list_value (scope_type& s, std::string d)
+ : value (s)
+ {
+ data.emplace_back (std::move (d));
+ }
+
+ // Note: stored in name as a directory.
+ //
+ list_value (scope_type& s, path d)
+ : value (s)
+ {
+ data.emplace_back (std::move (d));
+ }
+
names data;
};
typedef std::unique_ptr<list_value> list_value_ptr;
+
+ // value_proxy
+ //
+ struct value_proxy
+ {
+ explicit operator bool () const {return p != nullptr && *p != nullptr;}
+ explicit operator value_ptr& () const {return *p;}
+
+ // Get interface. See available specializations below.
+ //
+ template <typename T>
+ T
+ as () const = delete;
+
+ // Set interface.
+ //
+ const value_proxy&
+ operator= (value_ptr v) const
+ {
+ assert (v == nullptr || &v->scope == s);
+ *p = std::move (v);
+ return *this;
+ }
+
+ const value_proxy&
+ operator= (std::string v) const
+ {
+ // In most cases this is used to initialize a new variable, so
+ // don't bother trying to optimize for the case where p is not
+ // NULL.
+ //
+ p->reset (new list_value (*s, std::move (v)));
+ return *this;
+ }
+
+ // Note: stored in name as a directory.
+ //
+ const value_proxy&
+ operator= (path v) const
+ {
+ p->reset (new list_value (*s, std::move (v)));
+ return *this;
+ }
+
+ // Implementation details.
+ //
+ explicit
+ value_proxy (value_ptr* p, scope* s): p (p), s (s) {}
+
+ protected:
+ value_ptr* p;
+ scope* s;
+ };
+
+ template <>
+ list_value& value_proxy::
+ as<list_value&> () const;
+
+ template <>
+ const std::string& value_proxy::
+ as<const std::string&> () const;
+
+ // Note: get the name's directory.
+ //
+ template <>
+ const path& value_proxy::
+ as<const path&> () const;
}
namespace std
@@ -117,7 +200,28 @@ namespace build
}
};
- typedef prefix_map<variable_cref, value_ptr, '.'> variable_map;
+ struct variable_map: prefix_map<variable_cref, value_ptr, '.'>
+ {
+ typedef prefix_map<variable_cref, value_ptr, '.'> base;
+
+ value_proxy
+ operator[] (const std::string& v)
+ {
+ return operator[] (variable_pool.find (v));
+ }
+
+ value_proxy
+ operator[] (const variable& v)
+ {
+ return value_proxy (&base::operator[] (v), &scope_);
+ }
+
+ explicit
+ variable_map (scope& s): scope_ (s) {}
+
+ private:
+ scope& scope_; // Scope to which this map belongs (and all its value).
+ };
}
#endif // BUILD_VARIABLE
diff --git a/build/variable.cxx b/build/variable.cxx
index 613cada..56d5a08 100644
--- a/build/variable.cxx
+++ b/build/variable.cxx
@@ -4,9 +4,56 @@
#include <build/variable>
+#include <build/utility>
+
using namespace std;
namespace build
{
variable_set variable_pool;
+
+ // value_proxy
+ //
+ template <>
+ list_value& value_proxy::
+ as<list_value&> () const
+ {
+ list_value* lv (dynamic_cast<list_value*> (p->get ()));
+ assert (lv != nullptr);
+ return *lv;
+ }
+
+ template <>
+ const string& value_proxy::
+ as<const string&> () const
+ {
+ const list_value& lv (as<list_value&> ());
+ assert (lv.data.size () < 2);
+
+ if (lv.data.empty ())
+ return empty_string;
+
+ const name& n (lv.data.front ());
+
+ assert (n.type.empty () && n.dir.empty ());
+ return n.value;
+ }
+
+ // Note: get the name's directory.
+ //
+ template <>
+ const path& value_proxy::
+ as<const path&> () const
+ {
+ const list_value& lv (as<list_value&> ());
+ assert (lv.data.size () < 2);
+
+ if (lv.data.empty ())
+ return empty_path;
+
+ const name& n (lv.data.front ());
+
+ assert (n.type.empty () && n.value.empty ());
+ return n.dir;
+ }
}