From 1cc657a1ae2d496c0467a7c33257aa3cd2bfc3a8 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 3 Jun 2020 08:42:26 +0200 Subject: Add versioning for ad hoc C++ recipes This will allow us to deal with backward-incompatible changes to cxx_rule interface and semantics. --- libbuild2/parser.cxx | 23 +++++++++++++++++++++-- libbuild2/rule.cxx | 35 ++++++++++++++++++++++++++--------- libbuild2/rule.hxx | 19 +++++++++++-------- tests/dependency/recipe/testscript | 4 ++-- 4 files changed, 60 insertions(+), 21 deletions(-) diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx index 65a42fe..dada34d 100644 --- a/libbuild2/parser.cxx +++ b/libbuild2/parser.cxx @@ -1048,7 +1048,7 @@ namespace build2 // // % [] // [if|switch ...] - // {{ [] + // {{ [ ...] // ... // }} // @@ -1130,7 +1130,26 @@ namespace build2 { // C++ // - ar.reset (new adhoc_cxx_rule (loc, st.value.size ())); + + // Parse recipe version. + // + if (tt == type::newline || tt == type::eos) + fail (t) << "expected c++ recipe version instead of " << t; + + location nloc (get_location (t)); + names ns (parse_names (t, tt, pattern_mode::ignore)); + + uint64_t v; + try + { + v = convert (move (ns)); + } + catch (const invalid_argument& e) + { + fail (nloc) << "invalid c++ recipe version value: " << e; + } + + ar.reset (new adhoc_cxx_rule (loc, st.value.size (), v)); } else fail (lloc) << "unknown recipe language '" << *lang << "'"; diff --git a/libbuild2/rule.cxx b/libbuild2/rule.cxx index 7f9c357..9e2ef95 100644 --- a/libbuild2/rule.cxx +++ b/libbuild2/rule.cxx @@ -759,9 +759,9 @@ namespace build2 return target_state::changed; } - // cxx_rule + // cxx_rule_v1 // - bool cxx_rule:: + bool cxx_rule_v1:: match (action, target&, const string&) const { return true; @@ -770,6 +770,21 @@ namespace build2 // adhoc_cxx_rule // adhoc_cxx_rule:: + adhoc_cxx_rule (const location& l, size_t b, uint64_t v) + : adhoc_rule (l, b), version (v), impl (nullptr) + { + if (v != 1) + fail (l) << "unsupported c++ recipe version " << v; + } + + bool adhoc_cxx_rule:: + recipe_text (context&, string&& t, attributes&) + { + code = move (t); + return true; + } + + adhoc_cxx_rule:: ~adhoc_cxx_rule () { delete impl.load (memory_order_relaxed); // Serial execution. @@ -781,7 +796,7 @@ namespace build2 // @@ TODO: indentation is multi-line recipes is off (would need to insert // indentation after every newline). // - os << ind << string (braces, '{') << " c++" << endl + os << ind << string (braces, '{') << " c++ " << version << endl << ind << code << ind << string (braces, '}'); } @@ -829,7 +844,7 @@ namespace build2 if ((impl = this->impl.load (memory_order_relaxed)) != nullptr) break; - using create_function = cxx_rule* (const location&, target_state); + using create_function = cxx_rule_v1* (const location&, target_state); using load_function = create_function* (); // The only way to guarantee that the name of our module matches its @@ -945,7 +960,7 @@ namespace build2 ? loc.file.path.string () : loc.file.name ? *loc.file.name : string ()); - const string psig ("# c++ 1"); + const string psig ("# c++ " + to_string (version)); const string lsig ("// " + lf + ':' + to_string (loc.line)); // Check whether we need to (re)create the project. @@ -994,6 +1009,8 @@ namespace build2 // Include every header that can plausibly be needed by a rule. // + // @@ TMP: any new headers to add? [Keep this note for review.] + // ofs << "#include " << '\n' << "#include " << '\n' << "#include " << '\n' @@ -1033,7 +1050,7 @@ namespace build2 // ofs << "namespace" << '\n' << "{" << '\n' - << "class rule: public cxx_rule" << '\n' + << "class rule: public cxx_rule_v1" << '\n' << "{" << '\n' << "public:" << '\n' << '\n'; @@ -1041,7 +1058,7 @@ namespace build2 // Inherit base constructor. This way the user may provide their own // but don't have to. // - ofs << " using cxx_rule::cxx_rule;" << '\n' + ofs << " using cxx_rule_v1::cxx_rule_v1;" << '\n' << '\n'; // An extern "C" function cannot throw which can happen in case of a @@ -1049,7 +1066,7 @@ namespace build2 // We incorporate id to make sure it doesn't conflict with anything // user-defined. // - ofs << " static cxx_rule*" << '\n' + ofs << " static cxx_rule_v1*" << '\n' << " create_" << id << " (const location& l, target_state s)" << '\n' << " {" << '\n' << " return new rule (l, s);" << '\n' @@ -1082,7 +1099,7 @@ namespace build2 << "#ifdef _WIN32" << '\n' << "__declspec(dllexport)" << '\n' << "#endif" << '\n' - << "cxx_rule* (*" << sym << " ()) (const location&, target_state)" << '\n' + << "cxx_rule_v1* (*" << sym << " ()) (const location&, target_state)" << '\n' << "{" << '\n' << " return &rule_" << id << "::create_" << id << ";" << '\n' << "}" << '\n' diff --git a/libbuild2/rule.hxx b/libbuild2/rule.hxx index 05be9eb..76ab306 100644 --- a/libbuild2/rule.hxx +++ b/libbuild2/rule.hxx @@ -209,6 +209,13 @@ namespace build2 // class LIBBUILD2_SYMEXPORT cxx_rule: public rule { + // For now this class is provided purely as an alias for rule in case the + // implementation (which is also called rule) needs to refer to something + // in its base. + }; + + class LIBBUILD2_SYMEXPORT cxx_rule_v1: public cxx_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 @@ -218,7 +225,7 @@ namespace build2 const location recipe_loc; // Buildfile location of the recipe. const target_state recipe_state; // State of recipe library target. - cxx_rule (const location& l, target_state s) + cxx_rule_v1 (const location& l, target_state s) : recipe_loc (l), recipe_state (s) {} // Return true by default. @@ -241,15 +248,10 @@ namespace build2 virtual void dump (ostream&, string&) const override; - adhoc_cxx_rule (const location& l, size_t b) - : adhoc_rule (l, b), impl (nullptr) {} + adhoc_cxx_rule (const location&, size_t, uint64_t version); virtual bool - recipe_text (context&, string&& t, attributes&) override - { - code = move (t); - return true; - } + recipe_text (context&, string&& t, attributes&) override; virtual ~adhoc_cxx_rule () override; @@ -258,6 +260,7 @@ namespace build2 // Note that this recipe (rule instance) can be shared between multiple // targets which could all be matched in parallel. // + uint64_t version; string code; mutable atomic impl; }; diff --git a/tests/dependency/recipe/testscript b/tests/dependency/recipe/testscript index 5510e3c..c5e4a8c 100644 --- a/tests/dependency/recipe/testscript +++ b/tests/dependency/recipe/testscript @@ -76,14 +76,14 @@ EOE : $* <>/~%EOE% alias{x}: -{{ c++ +{{ c++ 1 void f (); }} dump alias{x} EOI :5:1: dump: % .+/alias\{x\}:% - {{ c++ + {{ c++ 1 void f (); }} EOE -- cgit v1.1