From 3b0df49b8828921edfb7b764b0628fb164dab852 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 9 Nov 2017 13:33:06 +0200 Subject: Initial support for prerequisite-specific variables, use for bin.whole --- build2/cc/link.cxx | 8 ++- build2/parser.cxx | 146 +++++++++++++++++++++++++++++++++++++++--------- build2/parser.hxx | 13 +++-- build2/prerequisite.cxx | 21 ++++++- build2/prerequisite.hxx | 27 ++++++++- build2/target.cxx | 2 + build2/target.hxx | 5 +- 7 files changed, 182 insertions(+), 40 deletions(-) diff --git a/build2/cc/link.cxx b/build2/cc/link.cxx index 4e22151..7f1e8d8 100644 --- a/build2/cc/link.cxx +++ b/build2/cc/link.cxx @@ -814,9 +814,11 @@ namespace build2 // that the variable is not overridable so we omit find_override() // calls. // - //@@ TODO: prerequisite-specific lookup. - // - lookup l (pt->find_original (var, true).first); + lookup l (p.prerequisite.vars[var]); + + if (!l.defined ()) + l = pt->find_original (var, true).first; + if (!l.defined ()) { bool g (pt->group != nullptr); diff --git a/build2/parser.cxx b/build2/parser.cxx index fb47ff6..a33f0a3 100644 --- a/build2/parser.cxx +++ b/build2/parser.cxx @@ -97,6 +97,12 @@ namespace build2 public: enter_target (): p_ (nullptr), t_ (nullptr) {} + enter_target (parser& p, target& t) + : p_ (&p), t_ (p.target_) + { + p.target_ = &t; + } + enter_target (parser& p, name&& n, // If n.pair, then o is out dir. name&& o, @@ -173,6 +179,38 @@ namespace build2 target* t_; }; + class parser::enter_prerequisite + { + public: + enter_prerequisite (): p_ (nullptr), r_ (nullptr) {} + + enter_prerequisite (parser& p, prerequisite& r) + : p_ (&p), r_ (p.prerequisite_) + { + assert (p.target_ != nullptr); + p.prerequisite_ = &r; + } + + ~enter_prerequisite () + { + if (p_ != nullptr) + p_->prerequisite_ = r_; + } + + // Note: move-assignable to empty only. + // + enter_prerequisite (enter_prerequisite&& x) {*this = move (x);} + enter_prerequisite& operator= (enter_prerequisite&& x) { + p_ = x.p_; r_ = x.r_; x.p_ = nullptr; return *this;} + + enter_prerequisite (const enter_prerequisite&) = delete; + enter_prerequisite& operator= (const enter_prerequisite&) = delete; + + private: + parser* p_; + prerequisite* r_; + }; + void parser:: parse_buildfile (istream& is, const path& p, scope& root, scope& base) { @@ -184,6 +222,7 @@ namespace build2 scope_ = &base; pbase_ = scope_->src_path_; target_ = nullptr; + prerequisite_ = nullptr; default_target_ = nullptr; enter_buildfile (p); // Needs scope_. @@ -205,9 +244,10 @@ namespace build2 { path_ = &l.name (); lexer_ = &l; - target_ = nullptr; scope_ = &s; pbase_ = scope_->src_path_; // Normally NULL. + target_ = nullptr; + prerequisite_ = nullptr; token t; type tt; @@ -223,9 +263,10 @@ namespace build2 { path_ = &l.name (); lexer_ = &l; - target_ = nullptr; scope_ = &s; pbase_ = b; + target_ = nullptr; + prerequisite_ = nullptr; token t; type tt; @@ -425,7 +466,7 @@ namespace build2 else attributes_pop (); - // @@ TODO: target scope. + // @@ TODO: target scope (also prerequisite scope below). } if (tt != type::rcbrace) @@ -446,8 +487,8 @@ namespace build2 // } - // Dependency declaration or scope/target-specific variable - // assignment. + // Dependency declaration (including prerequisite-specific variable + // assignment) or scope/target-specific variable assignment. // if (at.first) @@ -481,10 +522,8 @@ namespace build2 const variable& var ( var_pool.rw (*scope_).insert ( parse_variable_name (move (pns), ploc), - true)); // Allow overrides. + true /* overridable */)); - // Apply variable attributes. - // apply_variable_attributes (var); // If we have multiple targets/scopes, then we save the value @@ -604,8 +643,8 @@ namespace build2 } else { - // Existing value. What happens next depends what we are - // trying to do and what's already there. + // Existing value. What happens next depends on what we + // are trying to do and what's already there. // // Assignment is the easy one: we simply overwrite what's // already there. Also, if we are appending/prepending to @@ -657,7 +696,8 @@ namespace build2 rg.play (); // Replay. } } - // Dependency declaration. + // Dependency declaration potentially followed by prerequisite- + // specific variable assignment). // else { @@ -732,6 +772,63 @@ namespace build2 : prerequisite (p, memory_order_relaxed)); // Serial } } + + // Do we have prerequisite-specific variable assignment? + // + if (tt == type::colon) + { + // Set the variable in the last pns.size() prerequisites of each + // target. This code is similar to target-specific case above. + // + // @@ TODO prerequisite scope (also target scope above). + // + next (t, tt); + attributes_push (t, tt); + + // @@ PAT: currently we pattern-expand prerequisite-specific + // vars. + // + const location vloc (get_location (t)); + names vns (parse_names (t, tt, pattern_mode::expand)); + + if (tt != type::assign && + tt != type::prepend && + tt != type::append) + fail (t) << "expected assignment instead of " << t; + + type at (tt); + + const variable& var ( + var_pool.rw (*scope_).insert ( + parse_variable_name (move (vns), vloc), + true /* overridable */)); + + apply_variable_attributes (var); + + // We handle multiple targets and/or prerequisites by replaying + // the tokens (see the target-specific case above for details). + // + replay_guard rg (*this, tgs.size () > 1 || pns.size () > 1); + + for (auto ti (tgs.begin ()), te (tgs.end ()); ti != te; ) + { + target& tg (*ti); + enter_target tgg (*this, tg); + + for (size_t pn (tg.prerequisites_.size ()), + pi (pn - pns.size ()); pi != pn; ) + { + enter_prerequisite pg (*this, tg.prerequisites_[pi]); + parse_variable (t, tt, var, at); + + if (++pi != pn) + rg.play (); // Replay. + } + + if (++ti != te) + rg.play (); // Replay. + } + } } if (tt == type::newline) @@ -755,10 +852,8 @@ namespace build2 const variable& var ( var_pool.rw (*scope_).insert ( parse_variable_name (move (ns), nloc), - true)); // Allow overrides. + true /* overridable */)); - // Apply variable attributes. - // apply_variable_attributes (var); if (var.visibility == variable_visibility::target) @@ -1093,7 +1188,7 @@ namespace build2 auto& vp (var_pool.rw (*scope_)); if (p != string::npos) - var = &vp.insert (split (p), true); // Allow overrides. + var = &vp.insert (split (p), true /* overridable */); // // This could still be the 'foo =...' case. // @@ -1108,7 +1203,7 @@ namespace build2 (v[p = 0] == '=' || (n > 1 && v[0] == '+' && v[p = 1] == '='))) { - var = &vp.insert (move (t.value), true); // Allow overrides. + var = &vp.insert (move (t.value), true /* overridable */); next (t, tt); // Get the peeked token. split (p); // Returned name should be empty. } @@ -1117,8 +1212,6 @@ namespace build2 if (var != nullptr) { - // Apply variable attributes. - // apply_variable_attributes (*var); val = atype == type::assign @@ -1596,12 +1689,14 @@ namespace build2 value& lhs ( kind == type::assign - ? (target_ != nullptr - ? target_->assign (var) - : scope_->assign (var)) - : (target_ != nullptr - ? target_->append (var) - : scope_->append (var))); + + ? (prerequisite_ != nullptr ? prerequisite_->assign (var) : + target_ != nullptr ? target_->assign (var) : + /* */ scope_->assign (var)) + + : (prerequisite_ != nullptr ? prerequisite_->append (var, *target_) : + target_ != nullptr ? target_->append (var) : + /* */ scope_->append (var))); apply_value_attributes (&var, lhs, move (rhs), kind); } @@ -3907,9 +4002,10 @@ namespace build2 // lexer l (is, *path_, "\'\"\\$("); lexer_ = &l; - target_ = nullptr; scope_ = root_ = scope::global_; pbase_ = &work; // Use current working directory. + target_ = nullptr; + prerequisite_ = nullptr; // Turn on the buildspec mode/pairs recognition with '@' as the pair // separator (e.g., src_root/@out_root/exe{foo bar}). diff --git a/build2/parser.hxx b/build2/parser.hxx index 2ee0a64..90b01e0 100644 --- a/build2/parser.hxx +++ b/build2/parser.hxx @@ -20,6 +20,7 @@ namespace build2 { class scope; class target; + class prerequisite; class parser { @@ -356,6 +357,7 @@ namespace build2 protected: class enter_scope; class enter_target; + class enter_prerequisite; // Switch to a new current scope. Note that this function might also have // to switch to a new root scope if the new current scope is in another @@ -449,8 +451,8 @@ namespace build2 // Token saving and replaying. Note that it can only be used in certain // contexts. Specifically, the code that parses a replay must not interact - // with the lexer directly (e.g., the keyword() test). For now we don't - // enforce any of this. + // with the lexer directly (e.g., the keyword() test). Replays also cannot + // nest. For now we don't enforce any of this. // // Note also that the peeked token is not part of the replay, until it // is "got". @@ -573,9 +575,10 @@ namespace build2 const path* path_; // Current path. lexer* lexer_; - target* target_; // Current target, if any. - scope* scope_; // Current base scope (out_base). - scope* root_; // Current root scope (out_root). + prerequisite* prerequisite_; // Current prerequisite, if any. + target* target_; // Current target, if any. + scope* scope_; // Current base scope (out_base). + scope* root_; // Current root scope (out_root). const dir_path* pbase_ = nullptr; // Current pattern base directory. diff --git a/build2/prerequisite.cxx b/build2/prerequisite.cxx index 7facbaa..6233ec4 100644 --- a/build2/prerequisite.cxx +++ b/build2/prerequisite.cxx @@ -65,7 +65,8 @@ namespace build2 name (t.name), ext (to_ext (t.ext ())), scope (t.base_scope ()), - target (&t) + target (&t), + vars (false /* global */) { } @@ -75,4 +76,22 @@ namespace build2 const auto& p (t.prerequisites ()); return !(p.empty () || this < &p.front () || this > &p.back ()); } + + value& prerequisite:: + append (const variable& var, const target_type& t) + { + if (value* r = vars.find_to_modify (var)) + return *r; + + value& r (assign (var)); // NULL. + + // Note: pretty similar logic to target::append(). + // + lookup l (t.find_original (var).first); + + if (l.defined ()) + r = *l; // Copy value (and type) from the target/outer scope. + + return r; + } } diff --git a/build2/prerequisite.hxx b/build2/prerequisite.hxx index 77d96bf..eef91e6 100644 --- a/build2/prerequisite.hxx +++ b/build2/prerequisite.hxx @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -82,6 +83,23 @@ namespace build2 // mutable atomic target {nullptr}; + // Prerequisite-specific variables. + // + public: + variable_map vars; + + // Return a value suitable for assignment. See target for details. + // + value& + assign (const variable& var) {return vars.assign (var);} + + // Return a value suitable for appending. See target for details. Note + // that we have to explicitly pass the target that this prerequisite + // belongs to. + // + value& + append (const variable&, const target_type&); + public: prerequisite (optional p, const target_type_type& t, @@ -96,7 +114,8 @@ namespace build2 out (move (o)), name (move (n)), ext (move (e)), - scope (s) {} + scope (s), + vars (false /* global */) {} // Make a prerequisite from a target. // @@ -141,7 +160,8 @@ namespace build2 name (move (x.name)), ext (move (x.ext)), scope (move (x.scope)), - target (x.target.load (memory_order_relaxed)) {} + target (x.target.load (memory_order_relaxed)), + vars (move (x.vars)) {} prerequisite (const prerequisite& x, memory_order o = memory_order_consume) : proj (x.proj), @@ -151,7 +171,8 @@ namespace build2 name (x.name), ext (x.ext), scope (x.scope), - target (x.target.load (o)) {} + target (x.target.load (o)), + vars (x.vars) {} }; inline ostream& diff --git a/build2/target.cxx b/build2/target.cxx index 062db06..d81ccd5 100644 --- a/build2/target.cxx +++ b/build2/target.cxx @@ -247,6 +247,8 @@ namespace build2 value& target:: append (const variable& var) { + // Note: see also prerequisite::append() if changing anything here. + // Note that here we want the original value without any overrides // applied. // diff --git a/build2/target.hxx b/build2/target.hxx index ddff33b..0ed1aac 100644 --- a/build2/target.hxx +++ b/build2/target.hxx @@ -421,7 +421,7 @@ namespace build2 value& assign (const variable& var) {return vars.assign (var);} - // Return a value suitable for appending. See class scope for details. + // Return a value suitable for appending. See scope for details. // value& append (const variable&); @@ -699,8 +699,7 @@ namespace build2 target (dir_path d, dir_path o, string n) : dir (move (d)), out (move (o)), name (move (n)), - vars (false) // Note: not global. - {} + vars (false /* global */) {} }; // All targets are from the targets set below. -- cgit v1.1