From bc3c8492f129d9295c9ef6c325bf2c99e88ca73e Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 3 Jun 2020 08:39:36 +0200 Subject: Factor implementation-specific ad hoc recipe parsing to adhoc_*_rule --- libbuild2/forward.hxx | 5 +++ libbuild2/parser.cxx | 108 +++++++++++++++++++------------------------------- libbuild2/parser.hxx | 34 ++++++++-------- libbuild2/rule.cxx | 42 ++++++++++++++++++++ libbuild2/rule.hxx | 32 +++++++++++---- 5 files changed, 128 insertions(+), 93 deletions(-) diff --git a/libbuild2/forward.hxx b/libbuild2/forward.hxx index 1679b4a..4443610 100644 --- a/libbuild2/forward.hxx +++ b/libbuild2/forward.hxx @@ -70,6 +70,11 @@ namespace build2 // // class context; + + // + // + struct attribute; + struct attributes; } #endif // LIBBUILD2_FORWARD_HXX diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx index 72d189e..65a42fe 100644 --- a/libbuild2/parser.cxx +++ b/libbuild2/parser.cxx @@ -20,9 +20,6 @@ #include #include -#include -#include - #include // lookup_config using namespace std; @@ -33,7 +30,7 @@ namespace build2 using type = token_type; ostream& - operator<< (ostream& o, const parser::attribute& a) + operator<< (ostream& o, const attribute& a) { o << a.name; @@ -1111,24 +1108,9 @@ namespace build2 else fail (t) << "expected recipe language instead of " << t; - mode (lexer_mode::foreign, '\0', st.value.size ()); - next_after_newline (t, tt, st); // Should be on its own line. - - if (tt != type::word) - { - diag_record dr; - - dr << fail (t) << "unterminated recipe "; - if (kind.empty ()) dr << "block"; else dr << kind << "-block"; - - dr << info (st) << "recipe "; - if (kind.empty ()) dr << "block"; else dr << kind << "-block"; - dr << " starts here" << endf; - } - + shared_ptr ar; if (!skip) { - shared_ptr ar; if (d.first) { // Note that this is always the location of the opening multi- @@ -1138,72 +1120,62 @@ namespace build2 // location loc (get_location (st)); - // Buildscript - // if (!lang) { - // Handle and erase recipe-specific attributes. + // Buildscript // - optional diag; - for (auto i (d.as.begin ()); i != d.as.end (); ) - { - attribute& a (*i); - const string& n (a.name); - - if (n == "diag") - try - { - diag = convert (move (a.value)); - } - catch (const invalid_argument& e) - { - fail (d.as.loc) << "invalid " << n << " attribute value: " - << e; - } - else - { - ++i; - continue; - } - - i = d.as.erase (i); - } - - auto* asr (new adhoc_script_rule (loc, st.value.size ())); - ar.reset (asr); - - asr->checksum = sha256 (t.value).string (); - - istringstream is (move (t.value)); - build::script::parser p (ctx); - asr->script = p.pre_parse ( - is, asr->loc.file, loc.line + 1, move (diag)); + ar.reset (new adhoc_script_rule (loc, st.value.size ())); } - // - // C++ - // else if (*lang == "c++") { - ar.reset (new adhoc_cxx_rule ( - move (t.value), loc, st.value.size ())); - d.clean = true; + // C++ + // + ar.reset (new adhoc_cxx_rule (loc, st.value.size ())); } else fail (lloc) << "unknown recipe language '" << *lang << "'"; - // Verify we have no unhandled attributes. - // - for (attribute& a: d.as) - fail (d.as.loc) << "unknown recipe attribute " << a << endf; - assert (d.recipes[d.i] == nullptr); d.recipes[d.i] = ar; } else { + skip_line (t, tt); + assert (d.recipes[d.i] != nullptr); ar = d.recipes[d.i]; } + } + else + skip_line (t, tt); + + mode (lexer_mode::foreign, '\0', st.value.size ()); + next_after_newline (t, tt, st); // Should be on its own line. + + if (tt != type::word) + { + diag_record dr; + + dr << fail (t) << "unterminated recipe "; + if (kind.empty ()) dr << "block"; else dr << kind << "-block"; + + dr << info (st) << "recipe "; + if (kind.empty ()) dr << "block"; else dr << kind << "-block"; + dr << " starts here" << endf; + } + + if (!skip) + { + if (d.first) + { + if (ar->recipe_text (ctx, move (t.value), d.as)) + d.clean = true; + + // Verify we have no unhandled attributes. + // + for (attribute& a: d.as) + fail (d.as.loc) << "unknown recipe attribute " << a << endf; + } target_->adhoc_recipes.push_back ( adhoc_recipe {perform_update_id, move (ar)}); diff --git a/libbuild2/parser.hxx b/libbuild2/parser.hxx index 66a985a..88b4ad9 100644 --- a/libbuild2/parser.hxx +++ b/libbuild2/parser.hxx @@ -19,6 +19,23 @@ namespace build2 { + struct attribute + { + string name; + build2::value value; + }; + + ostream& + operator<< (ostream&, const attribute&); + + struct attributes: small_vector + { + location loc; // Start location. + + explicit + attributes (location l = {}): loc (move (l)) {} + }; + class LIBBUILD2_SYMEXPORT parser { public: @@ -261,23 +278,6 @@ namespace build2 // // In this example we only apply the value attributes after evaluating // the context, which has its own attributes. - // - struct attribute - { - string name; - build2::value value; - }; - - friend ostream& - operator<< (ostream&, const attribute&); - - struct attributes: small_vector - { - location loc; // Start location. - - explicit - attributes (location l = {}): loc (move (l)) {} - }; // Push a new entry into the attributes_ stack. If the next token is `[` // then parse the attribute sequence until ']' storing the result in the diff --git a/libbuild2/rule.cxx b/libbuild2/rule.cxx index 5f532ef..7f9c357 100644 --- a/libbuild2/rule.cxx +++ b/libbuild2/rule.cxx @@ -3,6 +3,8 @@ #include +#include + #include #include #include @@ -12,6 +14,8 @@ #include #include +#include // attributes + #include #include @@ -360,6 +364,44 @@ namespace build2 // adhoc_script_rule // + bool adhoc_script_rule:: + recipe_text (context& ctx, string&& t, attributes& as) + { + // Handle and erase recipe-specific attributes. + // + optional diag; + for (auto i (as.begin ()); i != as.end (); ) + { + attribute& a (*i); + const string& n (a.name); + + if (n == "diag") + try + { + diag = convert (move (a.value)); + } + catch (const invalid_argument& e) + { + fail (as.loc) << "invalid " << n << " attribute value: " << e; + } + else + { + ++i; + continue; + } + + i = as.erase (i); + } + + checksum = sha256 (t).string (); + + istringstream is (move (t)); + build::script::parser p (ctx); + script = p.pre_parse (is, loc.file, loc.line + 1, move (diag)); + + return false; + } + void adhoc_script_rule:: dump (ostream& os, string& ind) const { diff --git a/libbuild2/rule.hxx b/libbuild2/rule.hxx index 36e4733..05be9eb 100644 --- a/libbuild2/rule.hxx +++ b/libbuild2/rule.hxx @@ -116,7 +116,7 @@ namespace build2 // Ad hoc rule. // - // Note: not exported + // Note: not exported. // class adhoc_rule: public rule { @@ -129,6 +129,13 @@ namespace build2 braces (b), rule_match ("adhoc", static_cast (*this)) {} + // 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. + // + virtual bool + recipe_text (context&, string&&, attributes&) = 0; + public: // Some of the operations come in compensating pairs, such as update and // clean, install and uninstall. An ad hoc rule implementation may choose @@ -184,13 +191,16 @@ namespace build2 virtual void dump (ostream&, string&) const override; - using script_type = build::script::script; - adhoc_script_rule (const location& l, size_t b): adhoc_rule (l, b) {} + virtual bool + recipe_text (context&, string&&, attributes&) override; + public: + using script_type = build::script::script; + script_type script; - string checksum; // Script text hashsum. + string checksum; // Script text hash. }; // Ad hoc C++ rule. @@ -200,7 +210,6 @@ namespace build2 class LIBBUILD2_SYMEXPORT cxx_rule: public rule { public: - // A robust recipe may want to incorporate the recipe_state into its // up-to-date decision as if the recipe library was a prerequisite (it // cannot be injected as a real prerequisite since it's from a different @@ -232,8 +241,15 @@ namespace build2 virtual void dump (ostream&, string&) const override; - adhoc_cxx_rule (string c, const location& l, size_t b) - : adhoc_rule (l, b), code (move (c)), impl (nullptr) {} + adhoc_cxx_rule (const location& l, size_t b) + : adhoc_rule (l, b), impl (nullptr) {} + + virtual bool + recipe_text (context&, string&& t, attributes&) override + { + code = move (t); + return true; + } virtual ~adhoc_cxx_rule () override; @@ -242,7 +258,7 @@ namespace build2 // Note that this recipe (rule instance) can be shared between multiple // targets which could all be matched in parallel. // - const string code; + string code; mutable atomic impl; }; } -- cgit v1.1