aboutsummaryrefslogtreecommitdiff
path: root/build
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-04-29 13:17:30 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-04-29 13:17:30 +0200
commit6dbf3bbd2efa963859156826a25fc639c6c52ce5 (patch)
tree20cfd40a9b9649578a88b5eff7415a56eb14001d /build
parentc087efe145a75087acd646abe3258dad4aef4e2a (diff)
Add support for setting target-specific variables from buildfiles
Diffstat (limited to 'build')
-rw-r--r--build/dump.cxx76
-rw-r--r--build/parser8
-rw-r--r--build/parser.cxx232
-rw-r--r--build/variable9
-rw-r--r--build/variable.cxx9
-rw-r--r--build/variable.ixx8
6 files changed, 209 insertions, 133 deletions
diff --git a/build/dump.cxx b/build/dump.cxx
index 3f3bd93..df7d782 100644
--- a/build/dump.cxx
+++ b/build/dump.cxx
@@ -18,10 +18,38 @@ using namespace std;
namespace build
{
+ static bool
+ dump_variables (ostream& os, string& ind, const variable_map& vars)
+ {
+ bool r (false);
+
+ for (const auto& e: vars)
+ {
+ const variable& var (e.first);
+ const value_ptr& val (e.second);
+
+ os << endl
+ << ind << var.name << " = ";
+
+ if (val == nullptr)
+ os << "[null]";
+ else
+ {
+ //@@ TODO: assuming it is a list.
+ //
+ os << dynamic_cast<list_value&> (*val);
+ }
+
+ r = true;
+ }
+
+ return r;
+ }
+
static void
- dump_target (ostream& os, action a, const target& t)
+ dump_target (ostream& os, string& ind, action a, const target& t)
{
- os << t;
+ os << ind << t;
if (t.group != nullptr)
os << "->" << *t.group;
@@ -55,14 +83,27 @@ namespace build
first = false;
}
}
+
+ // Print target-specific variables.
+ //
+ if (!t.vars.empty ())
+ {
+ os << endl
+ << ind << '{';
+ ind += " ";
+ dump_variables (os, ind, t.vars);
+ ind.resize (ind.size () - 2);
+ os << endl
+ << ind << '}';
+ }
}
static void
dump_scope (ostream& os,
+ string& ind,
action a,
scope& p,
scope_map::iterator& i,
- string& ind,
set<const target*>& rts)
{
// We don't want the extra notations (e.g., ~/) provided by
@@ -81,25 +122,7 @@ namespace build
// Variables.
//
- for (const auto& e: p.vars)
- {
- const variable& var (e.first);
- const value_ptr& val (e.second);
-
- os << endl
- << ind << var.name << " = ";
-
- if (val == nullptr)
- os << "[null]";
- else
- {
- //@@ TODO: assuming it is a list.
- //
- os << dynamic_cast<list_value&> (*val);
- }
-
- vb = true;
- }
+ vb = dump_variables (os, ind, p.vars);
// Nested scopes of which we are a parent.
//
@@ -116,7 +139,7 @@ namespace build
os << endl;
scope& s (i->second);
- dump_scope (os, a, s, ++i, ind, rts);
+ dump_scope (os, ind, a, s, ++i, rts);
sb = true;
}
@@ -161,9 +184,8 @@ namespace build
vb = sb = false;
}
- os << endl
- << ind;
- dump_target (os, a, t);
+ os << endl;
+ dump_target (os, ind, a, t);
}
ind.resize (ind.size () - 2);
@@ -184,7 +206,7 @@ namespace build
set<const target*> rts;
ostream& os (*diag_stream);
- dump_scope (os, a, g, ++i, ind, rts);
+ dump_scope (os, ind, a, g, ++i, rts);
os << endl;
}
}
diff --git a/build/parser b/build/parser
index 94bf7f3..ccacb24 100644
--- a/build/parser
+++ b/build/parser
@@ -75,6 +75,9 @@ namespace build
void
variable (token&, token_type&, std::string name, token_type kind);
+ std::string
+ variable_name (names_type&&, const location&);
+
names_type
names (token& t, token_type& tt)
{
@@ -128,8 +131,9 @@ namespace build
private:
const std::string* path_; // Path processed by diag_relative().
lexer* lexer_;
- scope* scope_; // Current base scope (out_base).
- scope* root_; // Current root scope (out_root).
+ target* target_; // Current target, if any.
+ scope* scope_; // Current base scope (out_base).
+ scope* root_; // Current root scope (out_root).
const dir_path* out_root_;
const dir_path* src_root_;
target* default_target_;
diff --git a/build/parser.cxx b/build/parser.cxx
index 5c9e2cb..6a64d0e 100644
--- a/build/parser.cxx
+++ b/build/parser.cxx
@@ -41,6 +41,7 @@ namespace build
lexer l (is, rw);
lexer_ = &l;
+ target_ = nullptr;
scope_ = &base;
root_ = nullptr;
switch_root (&root);
@@ -63,6 +64,7 @@ namespace build
{
path_ = &l.name ();
lexer_ = &l;
+ target_ = nullptr;
scope_ = &s;
token t (type::eos, false, 0, 0);
@@ -137,7 +139,7 @@ namespace build
// ': foo' is equvalent to '{}: foo' and to 'dir{}: foo'.
//
- location nloc (get_location (t, &path_));
+ const location nloc (get_location (t, &path_));
names_type ns (tt != type::colon
? names (t, tt)
: names_type ({name ("dir", dir_path (), string ())}));
@@ -179,7 +181,7 @@ namespace build
{
// @@ TODO: point to name.
//
- fail (t) << "multiple names in directory scope";
+ fail (nloc) << "multiple names in directory scope";
}
dir = true;
@@ -240,7 +242,8 @@ namespace build
//
}
- // Dependency declaration.
+ // Dependency declaration or scope/target-specific variable
+ // assignment.
//
if (tt == type::name ||
tt == type::lcbrace ||
@@ -248,36 +251,14 @@ namespace build
tt == type::newline ||
tt == type::eos)
{
- location ploc (get_location (t, &path_));
+ const location ploc (get_location (t, &path_));
names_type pns (tt != type::newline && tt != type::eos
? names (t, tt)
: names_type ());
- // Prepare the prerequisite list.
+ // Common target entering code used in both cases.
//
- target::prerequisites_type ps;
- ps.reserve (pns.size ());
-
- for (auto& pn: pns)
- {
- const string* e;
- const target_type* ti (target_types.find (pn, e));
-
- if (ti == nullptr)
- fail (ploc) << "unknown target type " << pn.type;
-
- pn.dir.normalize ();
-
- // Find or insert.
- //
- prerequisite& p (
- scope_->prerequisites.insert (
- *ti, move (pn.dir), move (pn.value), e, *scope_, trace).first);
-
- ps.emplace_back (p);
- }
-
- for (auto& tn: ns)
+ auto enter_target = [this, &nloc, &trace] (name&& tn) -> target&
{
const string* e;
const target_type* ti (target_types.find (tn, e));
@@ -299,18 +280,95 @@ namespace build
// Find or insert.
//
- target& t (
- targets.insert (
- *ti, move (tn.dir), move (tn.value), e, trace).first);
+ return targets.insert (
+ *ti, move (tn.dir), move (tn.value), e, trace).first;
+ };
+
+ // Scope/target-specific variable assignment.
+ //
+ if (tt == type::equal || tt == type::plus_equal)
+ {
+ string var (variable_name (move (pns), ploc));
- //@@ OPT: move if last/single target (common cases).
+ // Enter the target/scope and set it as current.
//
- t.prerequisites.insert (t.prerequisites.end (),
- ps.begin (),
- ps.end ());
+ if (ns.size () != 1)
+ fail (nloc) << "multiple names in scope/target-specific "
+ << "variable assignment";
+
+ name& n (ns[0]);
+
+ target* ot (target_);
+ scope* os (scope_);
+
+ if (n.directory ())
+ {
+ // The same code as in directory scope handling code above.
+ //
+ dir_path p (move (n.dir));
+
+ if (p.relative ())
+ p = scope_->path () / p;
+
+ p.normalize ();
+ scope_ = &scopes[move (p)];
+ }
+ else
+ target_ = &enter_target (move (n));
- if (default_target_ == nullptr)
- default_target_ = &t;
+ type kind (tt);
+ next (t, tt);
+ variable (t, tt, move (var), kind);
+
+ scope_ = os;
+ target_ = ot;
+ }
+ // Dependency declaration.
+ //
+ else
+ {
+ // Prepare the prerequisite list.
+ //
+ target::prerequisites_type ps;
+ ps.reserve (pns.size ());
+
+ for (auto& pn: pns)
+ {
+ const string* e;
+ const target_type* ti (target_types.find (pn, e));
+
+ if (ti == nullptr)
+ fail (ploc) << "unknown target type " << pn.type;
+
+ pn.dir.normalize ();
+
+ // Find or insert.
+ //
+ prerequisite& p (
+ scope_->prerequisites.insert (
+ *ti,
+ move (pn.dir),
+ move (pn.value),
+ e,
+ *scope_,
+ trace).first);
+
+ ps.emplace_back (p);
+ }
+
+ for (auto& tn: ns)
+ {
+ target& t (enter_target (move (tn)));
+
+ //@@ OPT: move if last/single target (common cases).
+ //
+ t.prerequisites.insert (t.prerequisites.end (),
+ ps.begin (),
+ ps.end ());
+
+ if (default_target_ == nullptr)
+ default_target_ = &t;
+ }
}
if (tt == type::newline)
@@ -331,21 +389,11 @@ namespace build
//
if (tt == type::equal || tt == type::plus_equal)
{
- // LHS should be a single, simple name.
- //
- if (ns.size () != 1 || !ns[0].type.empty () || !ns[0].dir.empty ())
- fail (t) << "variable name expected before " << t;
-
- string name;
- if (ns[0].value.front () == '.') // Fully qualified name.
- name.assign (ns[0].value, 1, string::npos);
- else
- //@@ TODO: append namespace if any.
- name = move (ns[0].value);
+ string var (variable_name (move (ns), nloc));
type kind (tt);
next (t, tt);
- variable (t, tt, move (name), kind);
+ variable (t, tt, move (var), kind);
if (tt == type::newline)
next (t, tt);
@@ -367,7 +415,7 @@ namespace build
// The rest should be a list of buildfiles. Parse them as names
// to get variable expansion and directory prefixes.
//
- location l (get_location (t, &path_));
+ const location l (get_location (t, &path_));
names_type ns (tt != type::newline && tt != type::eos
? names (t, tt)
: names_type ());
@@ -450,7 +498,7 @@ namespace build
// The rest should be a list of buildfiles. Parse them as names
// to get variable expansion and directory prefixes.
//
- location l (get_location (t, &path_));
+ const location l (get_location (t, &path_));
names_type ns (tt != type::newline && tt != type::eos
? names (t, tt)
: names_type ());
@@ -607,7 +655,7 @@ namespace build
// The rest should be a list of projects and/or targets. Parse
// them as names to get variable expansion and directory prefixes.
//
- location l (get_location (t, &path_));
+ const location l (get_location (t, &path_));
names_type ns (tt != type::newline && tt != type::eos
? names (t, tt)
: names_type ());
@@ -645,7 +693,7 @@ namespace build
// The rest is a value. Parse it as names to get variable expansion.
//
- location l (get_location (t, &path_));
+ const location l (get_location (t, &path_));
export_value_ = (tt != type::newline && tt != type::eos
? names (t, tt)
: names_type ());
@@ -664,7 +712,7 @@ namespace build
// The rest should be a list of module names. Parse them as names
// to get variable expansion, etc.
//
- location l (get_location (t, &path_));
+ const location l (get_location (t, &path_));
names_type ns (tt != type::newline && tt != type::eos
? names (t, tt)
: names_type ());
@@ -704,58 +752,57 @@ namespace build
next (t, tt); // Swallow newline.
}
+ string parser::
+ variable_name (names_type&& ns, const location& l)
+ {
+ // The list should contain a single, simple name.
+ //
+ if (ns.size () != 1 || !ns[0].simple () || ns[0].empty ())
+ fail (l) << "variable name expected instead of " << ns;
+
+ string& n (ns[0].value);
+
+ if (n.front () == '.') // Fully qualified name.
+ return string (n, 1, string::npos);
+ else
+ //@@ TODO: append namespace if any.
+ return move (n);
+ }
+
void parser::
variable (token& t, token_type& tt, string name, token_type kind)
{
bool assign (kind == type::equal);
-
names_type vns (tt != type::newline && tt != type::eos
? names (t, tt)
: names_type ());
- // Enter the variable.
- //
const auto& var (variable_pool.find (move (name)));
if (assign)
{
- value_ptr& val (scope_->assign (var));
-
- if (val == nullptr) // Initialization.
- {
- val.reset (new list_value (move (vns)));
- }
- else // Assignment.
- {
- //@@ TODO: assuming it is a list.
- //
- dynamic_cast<list_value&> (*val) = move (vns);
- }
+ auto v (target_ != nullptr
+ ? target_->assign (var)
+ : scope_->assign (var));
+ v = move (vns);
}
else
{
- if (auto val = (*scope_)[var])
- {
- //@@ TODO: assuming it is a list.
- //
- list_value* lv (&val.as<list_value&> ());
+ auto v (target_ != nullptr
+ ? target_->append (var)
+ : scope_->append (var));
- if (!val.belongs (*scope_)) // Append to value from parent scope?
- {
- list_value_ptr nval (new list_value (*lv));
- lv = nval.get (); // Append to.
- scope_->vars.emplace (var, move (nval));
- }
-
- lv->insert (lv->end (),
- make_move_iterator (vns.begin ()),
- make_move_iterator (vns.end ()));
- }
- else // Initialization.
+ // More efficient than calling operator+=.
+ //
+ if (v)
{
- list_value_ptr nval (new list_value (move (vns)));
- scope_->vars.emplace (var, move (nval));
+ list_value& lv (v.as<list_value&> ());
+ lv.insert (lv.end (),
+ make_move_iterator (vns.begin ()),
+ make_move_iterator (vns.end ()));
}
+ else
+ v = move (vns); // Same as assignment.
}
}
@@ -943,9 +990,9 @@ namespace build
n = t.name ();
const auto& var (variable_pool.find (move (n)));
- auto val ((*scope_)[var]);
+ auto val (target_ != nullptr ? (*target_)[var] : (*scope_)[var]);
- // Undefined namespaces variables are not allowed.
+ // Undefined namespace variables are not allowed.
//
if (!val && var.name.find ('.') != string::npos)
fail (t) << "undefined namespace variable " << var.name;
@@ -1153,6 +1200,7 @@ namespace build
lexer l (is, name);
lexer_ = &l;
+ target_ = nullptr;
scope_ = root_ = global_scope;
// Turn on pairs recognition with '@' as the pair separator (e.g.,
@@ -1202,7 +1250,7 @@ namespace build
tt != type::pair_separator) // Empty pair LHS: '=foo ...'
fail (t) << "operation or target expected instead of " << t;
- location l (get_location (t, &path_)); // Start of names.
+ const location l (get_location (t, &path_)); // Start of names.
// This call will produce zero or more names and should stop
// at either tt_end or '('.
@@ -1267,7 +1315,7 @@ namespace build
// Inside '(' and ')' we have another buildspec.
//
next (t, tt);
- location l (get_location (t, &path_)); // Start of nested names.
+ const location l (get_location (t, &path_)); // Start of nested names.
buildspec nbs (buildspec_clause (t, tt, type::rparen));
// Merge the nested buildspec into ours. But first determine
diff --git a/build/variable b/build/variable
index 0ccd242..c353246 100644
--- a/build/variable
+++ b/build/variable
@@ -173,8 +173,13 @@ namespace build
as<const value&> () const {return **p;}
template <>
- list_value& value_proxy::
- as<list_value&> () const;
+ inline list_value& value_proxy::
+ as<list_value&> () const
+ {
+ list_value* lv (dynamic_cast<list_value*> (p->get ()));
+ assert (lv != nullptr);
+ return *lv;
+ }
template <>
inline const list_value& value_proxy::
diff --git a/build/variable.cxx b/build/variable.cxx
index 8f99ad9..ce1890d 100644
--- a/build/variable.cxx
+++ b/build/variable.cxx
@@ -15,15 +15,6 @@ namespace build
// 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
{
diff --git a/build/variable.ixx b/build/variable.ixx
index 54d7104..576d0cb 100644
--- a/build/variable.ixx
+++ b/build/variable.ixx
@@ -28,7 +28,13 @@ namespace build
inline const value_proxy& value_proxy::
operator= (list_value v) const
{
- p->reset (new list_value (std::move (v)));
+ if (*p == nullptr)
+ p->reset (new list_value (std::move (v)));
+ else
+ //@@ Assuming it is a list_value.
+ //
+ as<list_value&> () = std::move (v);
+
return *this;
}