From ea57c514dc169afb3ece21ff2e4c1d2ab0c47d6a Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 4 Aug 2021 06:45:02 +0200 Subject: Take into account file-base'ness in ad hoc buildscript recipes --- libbuild2/adhoc-rule-buildscript.cxx | 27 +++++++++++++++++++++++++-- libbuild2/adhoc-rule-buildscript.hxx | 13 ++++++++++--- libbuild2/adhoc-rule-cxx.cxx | 2 +- libbuild2/adhoc-rule-cxx.hxx | 5 ++++- libbuild2/build/script/parser.cxx | 10 ++++++++-- libbuild2/build/script/parser.hxx | 12 ++++++++---- libbuild2/build/script/parser.test.cxx | 2 +- libbuild2/parser.cxx | 12 +++++++++--- libbuild2/parser.hxx | 1 + libbuild2/rule.hxx | 11 +++++++++-- 10 files changed, 76 insertions(+), 19 deletions(-) diff --git a/libbuild2/adhoc-rule-buildscript.cxx b/libbuild2/adhoc-rule-buildscript.cxx index c715ff2..61b4cb2 100644 --- a/libbuild2/adhoc-rule-buildscript.cxx +++ b/libbuild2/adhoc-rule-buildscript.cxx @@ -23,7 +23,10 @@ using namespace std; namespace build2 { bool adhoc_buildscript_rule:: - recipe_text (const scope& s, string&& t, attributes& as) + recipe_text (const scope& s, + const target_type& tt, + string&& t, + attributes& as) { // Handle and erase recipe-specific attributes. // @@ -52,11 +55,12 @@ namespace build2 } checksum = sha256 (t).string (); + ttype = &tt; istringstream is (move (t)); build::script::parser p (s.ctx); - script = p.pre_parse (s, actions, + script = p.pre_parse (s, tt, actions, is, loc.file, loc.line + 1, move (diag), as.loc); @@ -109,6 +113,25 @@ namespace build2 perform_update_id) != actions.end (); } + bool adhoc_buildscript_rule:: + match (action a, target& t, const string& h, match_extra& me) const + { + // We pre-parsed the script with the assumption it will be used on a + // non/file-based target. Note that this should not be possible with + // patterns. + // + if (pattern == nullptr) + { + if ((t.is_a () != nullptr) != ttype->is_a ()) + { + fail (loc) << "incompatible target types used with shared recipe" << + info << "all targets must be file-based or non-file-based"; + } + } + + return adhoc_rule::match (a, t, h, me); + } + recipe adhoc_buildscript_rule:: apply (action a, target& t, match_extra& me) const { diff --git a/libbuild2/adhoc-rule-buildscript.hxx b/libbuild2/adhoc-rule-buildscript.hxx index 748a4c4..7f9c10a 100644 --- a/libbuild2/adhoc-rule-buildscript.hxx +++ b/libbuild2/adhoc-rule-buildscript.hxx @@ -25,6 +25,9 @@ namespace build2 virtual bool reverse_fallback (action, const target_type&) const override; + virtual bool + match (action, target&, const string&, match_extra&) const override; + virtual recipe apply (action, target&, match_extra&) const override; @@ -42,7 +45,10 @@ namespace build2 : adhoc_rule (move (n), l, b) {} virtual bool - recipe_text (const scope&, string&&, attributes&) override; + recipe_text (const scope&, + const target_type&, + string&&, + attributes&) override; virtual void dump_attributes (ostream&) const override; @@ -53,8 +59,9 @@ namespace build2 public: using script_type = build::script::script; - script_type script; - string checksum; // Script text hash. + script_type script; + string checksum; // Script text hash. + const target_type* ttype; // First target/pattern type. }; } diff --git a/libbuild2/adhoc-rule-cxx.cxx b/libbuild2/adhoc-rule-cxx.cxx index 83eeee0..83029ca 100644 --- a/libbuild2/adhoc-rule-cxx.cxx +++ b/libbuild2/adhoc-rule-cxx.cxx @@ -40,7 +40,7 @@ namespace build2 } bool adhoc_cxx_rule:: - recipe_text (const scope&, string&& t, attributes&) + recipe_text (const scope&, const target_type&, string&& t, attributes&) { code = move (t); return true; diff --git a/libbuild2/adhoc-rule-cxx.hxx b/libbuild2/adhoc-rule-cxx.hxx index 0ca6038..466c0e5 100644 --- a/libbuild2/adhoc-rule-cxx.hxx +++ b/libbuild2/adhoc-rule-cxx.hxx @@ -72,7 +72,10 @@ namespace build2 optional sep); virtual bool - recipe_text (const scope&, string&&, attributes&) override; + recipe_text (const scope&, + const target_type&, + string&&, + attributes&) override; virtual ~adhoc_cxx_rule () override; diff --git a/libbuild2/build/script/parser.cxx b/libbuild2/build/script/parser.cxx index b602880..43ec0d5 100644 --- a/libbuild2/build/script/parser.cxx +++ b/libbuild2/build/script/parser.cxx @@ -28,6 +28,7 @@ namespace build2 script parser:: pre_parse (const scope& bs, + const target_type& tt, const small_vector& as, istream& is, const path_name& pn, uint64_t line, optional diag, const location& diag_loc) @@ -48,6 +49,7 @@ namespace build2 pbase_ = scope_->src_path_; + file_based_ = tt.is_a (); perform_update_ = find (as.begin (), as.end (), perform_update_id) != as.end (); @@ -454,7 +456,7 @@ namespace build2 verify (); // Verify that depdb is not used for anything other than - // performing update. + // performing update on a file-based target. // assert (actions_ != nullptr); @@ -466,6 +468,10 @@ namespace build2 << ' ' << ctx.operation_table[a.operation ()]; } + if (!file_based_) + fail (l) << "'depdb' builtin can only be used for file-based " + << "targets"; + if (diag_line_) fail (diag_line_->second) << "'diag' builtin call before 'depdb' call" << @@ -1215,7 +1221,7 @@ namespace build2 void parser:: lookup_function (string&& name, const location& loc) { - if (perform_update_ && !impure_func_) + if (perform_update_ && file_based_ && !impure_func_) { const function_overloads* f (ctx.functions.find (name)); diff --git a/libbuild2/build/script/parser.hxx b/libbuild2/build/script/parser.hxx index 948c381..e744c08 100644 --- a/libbuild2/build/script/parser.hxx +++ b/libbuild2/build/script/parser.hxx @@ -46,7 +46,9 @@ namespace build2 // we will end up with mismatching diagnostics. // script - pre_parse (const scope&, const small_vector&, + pre_parse (const scope&, + const target_type&, + const small_vector&, istream&, const path_name&, uint64_t line, optional diag_name, const location& diag_loc); @@ -156,9 +158,11 @@ namespace build2 script* script_; const small_vector* actions_; // Non-NULL during pre-parse. - // True if performing update is one of the actions. Only set for the - // pre-parse mode. + // True if this script is for file-based targets and performing update + // is one of the actions, respectively. Only set for the pre-parse + // mode. // + bool file_based_; bool perform_update_; // Current low-verbosity script diagnostics and its weight. @@ -220,7 +224,7 @@ namespace build2 lines depdb_preamble_; // Note: excludes 'depdb clear'. // If present, the first impure function called in the body of the - // script that performs update. + // script that performs update of a file-based target. // // Note that during the line pre-parsing we cannot tell if this is a // body or depdb preamble line. Thus, if we encounter an impure diff --git a/libbuild2/build/script/parser.test.cxx b/libbuild2/build/script/parser.test.cxx index c9356a8..da23564 100644 --- a/libbuild2/build/script/parser.test.cxx +++ b/libbuild2/build/script/parser.test.cxx @@ -208,7 +208,7 @@ namespace build2 parser p (ctx); path_name nm ("buildfile"); - script s (p.pre_parse (tt.base_scope (), acts, + script s (p.pre_parse (tt.base_scope (), tt.type (), acts, cin, nm, 11 /* line */, (m != mode::diag diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx index a6f6ae6..076bcf5 100644 --- a/libbuild2/parser.cxx +++ b/libbuild2/parser.cxx @@ -1104,7 +1104,7 @@ namespace build2 if (tt == type::percent || tt == type::multi_lcbrace) { small_vector, 1> recipes; - parse_recipe (t, tt, token (t), recipes, rn); + parse_recipe (t, tt, token (t), recipes, ttype, rn); for (shared_ptr& pr: recipes) { @@ -1533,6 +1533,7 @@ namespace build2 parse_recipe (token& t, type& tt, const token& start, small_vector, 1>& recipes, + const target_type* ttype, const string& name) { // Parse a recipe chain. @@ -1581,6 +1582,7 @@ namespace build2 struct data { + const target_type* ttype; const string& name; small_vector, 1>& recipes; bool first; @@ -1589,7 +1591,7 @@ namespace build2 attributes& as; buildspec& bs; const location& bsloc; - } d {name, recipes, first, clean, i, as, bs, bsloc}; + } d {ttype, name, recipes, first, clean, i, as, bs, bsloc}; // Note that this function must be called at most once per iteration. // @@ -1797,7 +1799,11 @@ namespace build2 // Set the recipe text. // - if (ar.recipe_text (*scope_, move (t.value), d.as)) + if (ar.recipe_text ( + *scope_, + d.ttype != nullptr ? *d.ttype : target_->type (), + move (t.value), + d.as)) d.clean = true; // Verify we have no unhandled attributes. diff --git a/libbuild2/parser.hxx b/libbuild2/parser.hxx index 1095eeb..bc5bf4b 100644 --- a/libbuild2/parser.hxx +++ b/libbuild2/parser.hxx @@ -139,6 +139,7 @@ namespace build2 parse_recipe (token&, token_type&, const token&, small_vector, 1>&, + const target_type* = nullptr, const string& = {}); // Ad hoc target names inside < ... >. diff --git a/libbuild2/rule.hxx b/libbuild2/rule.hxx index ad6b51c..3eb7775 100644 --- a/libbuild2/rule.hxx +++ b/libbuild2/rule.hxx @@ -173,13 +173,20 @@ namespace build2 // Set the rule text, handle any recipe-specific attributes, and return // true if the recipe builds anything in the build/recipes/ directory and - // therefore requires cleanup. Scope is the scope of the recipe. + // therefore requires cleanup. + // + // Scope is the scope of the recipe and target type is the type of the + // first target (for ad hoc recipe) or primary group member type (for ad + // hoc pattern rule). The idea is that an implementation may make certain + // assumptions based on the first target type (e.g., file vs non-file + // based) in which case it should also enforce (e.g., in match()) that any + // other targets that share this recipe are also of suitable type. // // Note also that this function is called after the actions member has // been populated. // virtual bool - recipe_text (const scope&, string&&, attributes&) = 0; + recipe_text (const scope&, const target_type&, string&&, attributes&) = 0; public: // Some of the operations come in compensating pairs, such as update and -- cgit v1.1