From 9ca783f9fab41eac40b96313749533ea5c965426 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 28 Jun 2018 11:04:24 +0200 Subject: Add prerequisite variable visibility specification/enforcement --- build2/context.cxx | 5 +++-- build2/parser.cxx | 46 ++++++++++++++++++++++++++++++++++++---------- build2/prerequisite.hxx | 2 ++ build2/scope.cxx | 6 ++++++ build2/test/init.cxx | 8 ++++---- build2/variable.cxx | 19 +++++++++++++++++++ build2/variable.hxx | 41 ++++++++++++++++++++++++++++++++++++----- 7 files changed, 106 insertions(+), 21 deletions(-) diff --git a/build2/context.cxx b/build2/context.cxx index 4884145..5a94772 100644 --- a/build2/context.cxx +++ b/build2/context.cxx @@ -693,6 +693,7 @@ namespace build2 { auto v_p (variable_visibility::project); auto v_t (variable_visibility::target); + auto v_q (variable_visibility::prereq); var_src_root = &vp.insert ("src_root"); var_out_root = &vp.insert ("out_root"); @@ -714,9 +715,9 @@ namespace build2 var_import_target = &vp.insert ("import.target"); - var_clean = &vp.insert ("clean", v_t); + var_clean = &vp.insert ("clean", v_t); var_backlink = &vp.insert ("backlink", v_t); - var_include = &vp.insert ("include", v_t); + var_include = &vp.insert ("include", v_q); vp.insert (var_extension, v_t); diff --git a/build2/parser.cxx b/build2/parser.cxx index dfa9916..4fe30e9 100644 --- a/build2/parser.cxx +++ b/build2/parser.cxx @@ -544,6 +544,12 @@ namespace build2 const variable& var (parse_variable_name (move (pns), ploc)); apply_variable_attributes (var); + if (var.visibility >= variable_visibility::prereq) + { + fail (nloc) << "variable " << var << " has " << var.visibility + << " visibility but is assigned in a target"; + } + // If we have multiple targets, then we save the value tokens when // parsing the first one and then replay them for the subsequent. // We have to do it this way because the value may contain @@ -898,10 +904,16 @@ namespace build2 const variable& var (parse_variable_name (move (ns), nloc)); apply_variable_attributes (var); - if (var.visibility == variable_visibility::target) - fail (nloc) << "variable " << var << " has target visibility but " - << "assigned in a scope" << - info << "consider changing to '*: " << var << "'"; + if (var.visibility >= variable_visibility::target) + { + diag_record dr (fail (nloc)); + + dr << "variable " << var << " has " << var.visibility + << " visibility but is assigned in a scope"; + + if (var.visibility == variable_visibility::target) + dr << info << "consider changing it to '*: " << var << "'"; + } { enter_scope sg (d.empty () @@ -1325,6 +1337,8 @@ namespace build2 // auto at (attributes_push (t, tt)); + const location vloc (get_location (t)); + if (tt == type::word) { // Split the token into the variable name and value at position (p) of @@ -1395,6 +1409,12 @@ namespace build2 { apply_variable_attributes (*var); + if (var->visibility >= variable_visibility::target) + { + fail (vloc) << "variable " << *var << " has " << var->visibility + << " visibility but is assigned in import"; + } + val = atype == type::assign ? &scope_->assign (*var) : &scope_->append (*var); @@ -1770,9 +1790,11 @@ namespace build2 const variable& var (parse_variable_name (move (vns), vloc)); apply_variable_attributes (var); - if (var.visibility == variable_visibility::target) - fail (vloc) << "variable " << var << " has target visibility but " - << "assigned in for-loop"; + if (var.visibility >= variable_visibility::target) + { + fail (vloc) << "variable " << var << " has " << var.visibility + << " visibility but is assigned in for-loop"; + } // Now the value (list of names) to iterate over. Parse it as a variable // value to get expansion, attributes, etc. @@ -4701,9 +4723,13 @@ namespace build2 // const auto& var (var_pool.rw (*scope_).insert (move (name), true)); - if (target_ == nullptr && var.visibility == variable_visibility::target) - fail (loc) << "variable " << var << " has target visibility but " - << "expanded in a scope"; + if ((var.visibility == variable_visibility::prereq) || + (var.visibility == variable_visibility::target && target_ == nullptr)) + { + fail (loc) << "variable " << var << " has " << var.visibility + << " visibility but is expanded in a " + << (target_ != nullptr ? "target" : "scope"); + } return target_ != nullptr ? (*target_)[var] : (*scope_)[var]; diff --git a/build2/prerequisite.hxx b/build2/prerequisite.hxx index 6ae6ec7..b8707ce 100644 --- a/build2/prerequisite.hxx +++ b/build2/prerequisite.hxx @@ -84,6 +84,8 @@ namespace build2 // Prerequisite-specific variables. // + // Note that the lookup is often ad hoc (see bin.whole as an example). + // public: variable_map vars; diff --git a/build2/scope.cxx b/build2/scope.cxx index ff004b5..dc08925 100644 --- a/build2/scope.cxx +++ b/build2/scope.cxx @@ -23,6 +23,9 @@ namespace build2 size_t d (0); + if (var.visibility == variable_visibility::prereq) + return make_pair (lookup (), d); + // Process target type/pattern-specific prepend/append values. // auto pre_app = [&var] (lookup& l, @@ -164,6 +167,8 @@ namespace build2 case variable_visibility::normal: s = s->parent_scope (); break; + case variable_visibility::prereq: + assert (false); } } @@ -247,6 +252,7 @@ namespace build2 case variable_visibility::normal: break; case variable_visibility::target: + case variable_visibility::prereq: assert (false); } diff --git a/build2/test/init.cxx b/build2/test/init.cxx index 66889f7..822a444 100644 --- a/build2/test/init.cxx +++ b/build2/test/init.cxx @@ -85,10 +85,10 @@ namespace build2 // appending the input paths to test.arguments or by passing them in a // separate test.inputs variable). // - vp.insert ("test.stdin", variable_visibility::target), - vp.insert ("test.stdout", variable_visibility::target), - vp.insert ("test.roundtrip", variable_visibility::target), - vp.insert ("test.input", variable_visibility::target), + vp.insert ("test.stdin", variable_visibility::prereq), + vp.insert ("test.stdout", variable_visibility::prereq), + vp.insert ("test.roundtrip", variable_visibility::prereq), + vp.insert ("test.input", variable_visibility::prereq), // Test target platform. // diff --git a/build2/variable.cxx b/build2/variable.cxx index 0c07db6..406123d 100644 --- a/build2/variable.cxx +++ b/build2/variable.cxx @@ -13,6 +13,25 @@ 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:: diff --git a/build2/variable.hxx b/build2/variable.hxx index 4d11b25..e11d00c 100644 --- a/build2/variable.hxx +++ b/build2/variable.hxx @@ -94,17 +94,48 @@ namespace build2 bool (*const empty) (const value&); }; - enum class variable_visibility + // The order of the enumerators is arranged so that their integral values + // indicate whether one is more restrictive than the other. + // + enum class variable_visibility: uint8_t { // Note that the search for target type/pattern-specific terminates at // the project boundary. // - target, // Target and target type/pattern-specific. - scope, // This scope (no outer scopes). - project, // This project (no outer projects). - normal // All outer scopes. + normal, // All outer scopes. + project, // This project (no outer projects). + scope, // This scope (no outer scopes). + target, // Target and target type/pattern-specific. + prereq // Prerequisite-specific. }; + inline bool + operator> (variable_visibility l, variable_visibility r) + { + return static_cast (l) > static_cast (r); + } + + inline bool + operator>= (variable_visibility l, variable_visibility r) + { + return static_cast (l) >= static_cast (r); + } + + inline bool + operator< (variable_visibility l, variable_visibility r) + { + return r > l; + } + + inline bool + operator<= (variable_visibility l, variable_visibility r) + { + return r >= l; + } + + ostream& + operator<< (ostream&, variable_visibility); + // variable // // The two variables are considered the same if they have the same name. -- cgit v1.1