From 6dbf3bbd2efa963859156826a25fc639c6c52ce5 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 29 Apr 2015 13:17:30 +0200 Subject: Add support for setting target-specific variables from buildfiles --- build/dump.cxx | 76 +++++++++++------- build/parser | 8 +- build/parser.cxx | 232 ++++++++++++++++++++++++++++++++--------------------- build/variable | 9 ++- build/variable.cxx | 9 --- build/variable.ixx | 8 +- 6 files changed, 209 insertions(+), 133 deletions(-) (limited to 'build') 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 (*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& 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 (*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 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 (*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 ()); + 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 ()); + 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 {return **p;} template <> - list_value& value_proxy:: - as () const; + inline list_value& value_proxy:: + as () const + { + list_value* lv (dynamic_cast (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 () const - { - list_value* lv (dynamic_cast (p->get ())); - assert (lv != nullptr); - return *lv; - } - - template <> const string& value_proxy:: as () 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 () = std::move (v); + return *this; } -- cgit v1.1