From 968d8a7acd6c23078a3ea6936c03be0b45523227 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 7 Feb 2018 15:48:50 +0200 Subject: Add support for update-for-{test,install} operation aliases --- build2/b.cxx | 79 ++++++++++++++++++++++++++------------------ build2/config/init.cxx | 4 +-- build2/config/operation.cxx | 2 +- build2/context.cxx | 16 +++++---- build2/context.hxx | 8 ++--- build2/dist/operation.cxx | 32 ++++++++++++------ build2/install/init.cxx | 7 ++-- build2/install/operation.cxx | 18 ++++++++++ build2/install/operation.hxx | 1 + build2/operation.cxx | 10 +++++- build2/operation.hxx | 39 ++++++++++++++-------- build2/parser.cxx | 4 +-- build2/test/init.cxx | 3 +- build2/test/operation.cxx | 17 ++++++++++ build2/test/operation.hxx | 1 + build2/test/rule.cxx | 2 +- 16 files changed, 165 insertions(+), 78 deletions(-) diff --git a/build2/b.cxx b/build2/b.cxx index b827470..6d681cd 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -577,23 +577,22 @@ main (int argc, char* argv[]) // values& mparams (lifted == nullptr ? mit->params : lifted->params); { - const string& mname (lifted == nullptr ? mit->name : lifted->name); - current_mname = &mname; + current_mname = (lifted == nullptr ? mit->name : lifted->name); - if (!mname.empty ()) + if (!current_mname.empty ()) { - if (meta_operation_id m = meta_operation_table.find (mname)) + if (meta_operation_id m = meta_operation_table.find (current_mname)) { // Can modify params, opspec, change meta-operation name. // if (auto f = meta_operation_table[m].process) - current_mname = &f ( + current_mname = f ( var_ovs, mparams, opspecs, lifted != nullptr, l); } } } - const string& mname (*current_mname); + const string& mname (current_mname); for (auto oit (opspecs.begin ()); oit != opspecs.end (); ++oit) { @@ -601,38 +600,39 @@ main (int argc, char* argv[]) // A lifted meta-operation will always have default operation. // - const string& oname (lifted == nullptr ? os.name : string ()); + current_oname = (lifted == nullptr ? os.name : empty_string); + const string& oname (current_oname); const values& oparams (lifted == nullptr ? os.params : values ()); - current_oname = &oname; - if (lifted != nullptr) lifted = nullptr; // Clear for the next iteration. if (os.empty ()) // Default target: dir{}. os.push_back (targetspec (name ("dir", string ()))); - operation_id oid (0); // Not yet translated. + operation_id oid (0), orig_oid; const operation_info* oif (nullptr); + const operation_info* outer_oif (nullptr); - operation_id pre_oid (0); + operation_id pre_oid (0), orig_pre_oid; const operation_info* pre_oif (nullptr); - operation_id post_oid (0); + operation_id post_oid (0), orig_post_oid; const operation_info* post_oif (nullptr); // Return true if this operation is lifted. // - auto lift = [&oname, &mname, &os, &mit, &lifted, &skip, &l, &trace] () + auto lift = [&os, &mit, &lifted, &skip, &l, &trace] () { - meta_operation_id m (meta_operation_table.find (oname)); + meta_operation_id m (meta_operation_table.find (current_oname)); if (m != 0) { - if (!mname.empty ()) - fail (l) << "nested meta-operation " << mname << '(' << oname << ')'; + if (!current_mname.empty ()) + fail (l) << "nested meta-operation " << current_mname << '(' + << current_oname << ')'; - l5 ([&]{trace << "lifting operation " << oname + l5 ([&]{trace << "lifting operation " << current_oname << ", id " << uint16_t (m);}); lifted = &os; @@ -991,33 +991,44 @@ main (int argc, char* argv[]) if (o == 0) o = default_id; + // Before de-aliasing and/or translation (we assume in the check + // below that those will be the same since we've verified the + // meta-operation implementation is the same). + // + orig_oid = o; + oif = lookup (o); l5 ([&]{trace << "start operation batch " << oif->name - << ", id " << static_cast (o);}); + << ", id " << static_cast (oif->id);}); // Allow the meta-operation to translate the operation. // if (mif->operation_pre != nullptr) - oid = mif->operation_pre (mparams, o); + oid = mif->operation_pre (mparams, oif->id); else // Otherwise translate default to update. - oid = (o == default_id ? update_id : o); + oid = (oif->id == default_id ? update_id : oif->id); - if (o != oid) + if (oif->id != oid) { oif = lookup (oid); + oid = oif->id; // De-alias. l5 ([&]{trace << "operation translated to " << oif->name << ", id " << static_cast (oid);}); } + if (oif->outer_id != 0) + outer_oif = lookup (oif->outer_id); + // Handle pre/post operations. // if (oif->pre != nullptr) { - if ((pre_oid = oif->pre (oparams, mid, l)) != 0) + if ((orig_pre_oid = oif->pre (oparams, mid, l)) != 0) { - assert (pre_oid != default_id); - pre_oif = lookup (pre_oid); + assert (orig_pre_oid != default_id); + pre_oif = lookup (orig_pre_oid); + pre_oid = pre_oif->id; // De-alias. } } else if (!oparams.empty ()) @@ -1026,10 +1037,11 @@ main (int argc, char* argv[]) if (oif->post != nullptr) { - if ((post_oid = oif->post (oparams, mid)) != 0) + if ((orig_post_oid = oif->post (oparams, mid)) != 0) { - assert (post_oid != default_id); - post_oif = lookup (post_oid); + assert (orig_post_oid != default_id); + post_oif = lookup (orig_post_oid); + post_oid = post_oif->id; } } } @@ -1053,13 +1065,16 @@ main (int argc, char* argv[]) << i->name << " in the same operation batch"; }; - check (oid, oif); + check (orig_oid, oif); + + if (oif->outer_id != 0) + check (oif->outer_id, outer_oif); if (pre_oid != 0) - check (pre_oid, pre_oif); + check (orig_pre_oid, pre_oif); if (post_oid != 0) - check (post_oid, post_oif); + check (orig_post_oid, post_oif); } } @@ -1246,9 +1261,9 @@ main (int argc, char* argv[]) tgs.reset (); } - set_current_oif (*oif); + set_current_oif (*oif, outer_oif); - action a (mid, oid, 0); + action a (mid, oid, oif->outer_id); { result_printer p (tgs); diff --git a/build2/config/init.cxx b/build2/config/init.cxx index 35bc94c..8bbc07e 100644 --- a/build2/config/init.cxx +++ b/build2/config/init.cxx @@ -29,8 +29,8 @@ namespace build2 l5 ([&]{trace << "for " << rs.out_path ();}); - const string& mname (*current_mname); - const string& oname (*current_oname); + const string& mname (current_mname); + const string& oname (current_oname); // Only create the module if we are configuring or creating. This is a // bit tricky since the build2 core may not yet know if this is the diff --git a/build2/config/operation.cxx b/build2/config/operation.cxx index fa7cb9f..d5a1565 100644 --- a/build2/config/operation.cxx +++ b/build2/config/operation.cxx @@ -732,7 +732,7 @@ namespace build2 fail (l) << "invalid module name: " << e.what (); } - current_oname = &empty_string; // Make sure valid. + current_oname = empty_string; // Make sure valid. // Now handle each target in each operation spec. // diff --git a/build2/context.cxx b/build2/context.cxx index 6f3abcc..e73f0a1 100644 --- a/build2/context.cxx +++ b/build2/context.cxx @@ -341,8 +341,8 @@ namespace build2 const char var_extension[10] = "extension"; - const string* current_mname; - const string* current_oname; + string current_mname; + string current_oname; const meta_operation_info* current_mif; const operation_info* current_inner_oif; @@ -393,8 +393,10 @@ namespace build2 operation_table.insert ("update"); operation_table.insert ("clean"); operation_table.insert ("test"); + operation_table.insert ("update-for-test"); operation_table.insert ("install"); operation_table.insert ("uninstall"); + operation_table.insert ("update-for-install"); // Create global scope. Note that the empty path is a prefix for any other // path. See the comment in for details. @@ -762,7 +764,7 @@ namespace build2 { r = m.name_do; - if (!io.name_doing.empty ()) + if (io.name_doing[0] != '\0') { r += ' '; r += io.name_doing; @@ -800,7 +802,7 @@ namespace build2 if (!m.name_doing.empty ()) r = m.name_doing; - if (!io.name_doing.empty ()) + if (io.name_doing[0] != '\0') { if (!r.empty ()) r += ' '; r += io.name_doing; @@ -838,7 +840,7 @@ namespace build2 { r = m.name_did; - if (!io.name_doing.empty ()) + if (io.name_doing[0] != '\0') { r += ' '; r += io.name_doing; @@ -877,7 +879,7 @@ namespace build2 { os << t; - if (!io.name_done.empty ()) + if (io.name_done[0] != '\0') os << ' ' << io.name_done; if (oo != nullptr) @@ -885,7 +887,7 @@ namespace build2 } else { - if (!io.name_doing.empty ()) + if (io.name_doing[0] != '\0') os << io.name_doing << ' '; if (oo != nullptr) diff --git a/build2/context.hxx b/build2/context.hxx index 97ba285..7702564 100644 --- a/build2/context.hxx +++ b/build2/context.hxx @@ -285,8 +285,8 @@ namespace build2 // lifted. The name is always for an outer operation (or meta operation // that hasn't been recognized as such yet). // - extern const string* current_mname; - extern const string* current_oname; + extern string current_mname; + extern string current_oname; extern const meta_operation_info* current_mif; extern const operation_info* current_inner_oif; @@ -315,7 +315,7 @@ namespace build2 inline void set_current_mif (const meta_operation_info& mif) { - current_mname = &mif.name; + current_mname = mif.name; current_mif = &mif; current_on = 0; // Reset. } @@ -324,7 +324,7 @@ namespace build2 set_current_oif (const operation_info& inner_oif, const operation_info* outer_oif = nullptr) { - current_oname = &(outer_oif == nullptr ? inner_oif : *outer_oif).name; + current_oname = (outer_oif == nullptr ? inner_oif : *outer_oif).name; current_inner_oif = &inner_oif; current_outer_oif = outer_oif; current_on++; diff --git a/build2/dist/operation.cxx b/build2/dist/operation.cxx index a378b5d..e0d1f60 100644 --- a/build2/dist/operation.cxx +++ b/build2/dist/operation.cxx @@ -126,15 +126,25 @@ namespace build2 { if (const operation_info* oif = rs->operations[id]) { + // Skip aliases (e.g., update-for-install). In fact, one can argue + // the default update should be sufficient since it is assumed to + // update all prerequisites and we not longer support ad hoc stuff + // like test.input. + // + if (oif->id != id) + continue; + // Use standard (perform) match. // if (oif->pre != nullptr) { - const operation_info* poif ( - rs->operations[oif->pre (params, dist_id, loc)]); - set_current_oif (*poif, oif); - action a (dist_id, poif->id, oif->id); - match (params, a, ts, true /* quiet */); + if (operation_id pid = oif->pre (params, dist_id, loc)) + { + const operation_info* poif (rs->operations[pid]); + set_current_oif (*poif, oif); + action a (dist_id, poif->id, oif->id); + match (params, a, ts, true /* quiet */); + } } set_current_oif (*oif); @@ -143,11 +153,13 @@ namespace build2 if (oif->post != nullptr) { - const operation_info* poif ( - rs->operations[oif->post (params, dist_id)]); - set_current_oif (*poif, oif); - action a (dist_id, poif->id, oif->id); - match (params, a, ts, true /* quiet */); + if (operation_id pid = oif->post (params, dist_id)) + { + const operation_info* poif (rs->operations[pid]); + set_current_oif (*poif, oif); + action a (dist_id, poif->id, oif->id); + match (params, a, ts, true /* quiet */); + } } } } diff --git a/build2/install/init.cxx b/build2/install/init.cxx index 259fb07..52875d4 100644 --- a/build2/install/init.cxx +++ b/build2/install/init.cxx @@ -140,10 +140,11 @@ namespace build2 if (!function_family::defined ("install")) functions (); - // Register the install and uninstall operations. + // Register our operations. // - r.operations.insert (install_id, op_install); - r.operations.insert (uninstall_id, op_uninstall); + r.operations.insert (install_id, op_install); + r.operations.insert (uninstall_id, op_uninstall); + r.operations.insert (update_for_install_id, op_update_for_install); return false; } diff --git a/build2/install/operation.cxx b/build2/install/operation.cxx index 5279b3d..be4dd2d 100644 --- a/build2/install/operation.cxx +++ b/build2/install/operation.cxx @@ -30,6 +30,7 @@ namespace build2 const operation_info op_install { install_id, + 0, "install", "install", "installing", @@ -52,6 +53,7 @@ namespace build2 // const operation_info op_uninstall { uninstall_id, + 0, "uninstall", "uninstall", "uninstalling", @@ -62,5 +64,21 @@ namespace build2 &install_pre, nullptr }; + + // Also the explicit update-for-install operation alias. + // + const operation_info op_update_for_install { + update_id, // Note: not update_for_install_id. + install_id, + op_update.name, + op_update.name_do, + op_update.name_doing, + op_update.name_did, + op_update.name_done, + op_update.mode, + op_update.concurrency, + op_update.pre, + op_update.post + }; } } diff --git a/build2/install/operation.hxx b/build2/install/operation.hxx index 0d96faf..1f3db58 100644 --- a/build2/install/operation.hxx +++ b/build2/install/operation.hxx @@ -16,6 +16,7 @@ namespace build2 { extern const operation_info op_install; extern const operation_info op_uninstall; + extern const operation_info op_update_for_install; } } diff --git a/build2/operation.cxx b/build2/operation.cxx index a43e20a..f262321 100644 --- a/build2/operation.cxx +++ b/build2/operation.cxx @@ -522,6 +522,7 @@ namespace build2 // const operation_info op_default { default_id, + 0, "", "", "", @@ -533,8 +534,14 @@ namespace build2 nullptr }; - const operation_info op_update { +#ifndef _MSC_VER + constexpr +#else + const +#endif + operation_info op_update { update_id, + 0, "update", "update", "updating", @@ -548,6 +555,7 @@ namespace build2 const operation_info op_clean { clean_id, + 0, "clean", "clean", "cleaning", diff --git a/build2/operation.hxx b/build2/operation.hxx index e79a57c..7f59783 100644 --- a/build2/operation.hxx +++ b/build2/operation.hxx @@ -127,12 +127,16 @@ namespace build2 // that no operation was explicitly specified by the user. If adding // something here remember to update the man page. // - const operation_id default_id = 1; // Shall be first. - const operation_id update_id = 2; // Shall be second. - const operation_id clean_id = 3; - const operation_id test_id = 4; - const operation_id install_id = 5; - const operation_id uninstall_id = 6; + const operation_id default_id = 1; // Shall be first. + const operation_id update_id = 2; // Shall be second. + const operation_id clean_id = 3; + + const operation_id test_id = 4; + const operation_id update_for_test_id = 5; // update(for test) alias. + + const operation_id install_id = 6; + const operation_id uninstall_id = 7; + const operation_id update_for_install_id = 8; // update(for install) alias. const action_id perform_update_id = (perform_id << 4) | update_id; const action_id perform_clean_id = (perform_id << 4) | clean_id; @@ -321,10 +325,18 @@ namespace build2 // Operation info. // + // NOTE: keep POD-like to ensure can be constant-initialized in order to + // sidestep static initialization order (relied upon in operation + // aliasing). + // struct operation_info { + // If outer_id is not 0, then use that as the outer part of the + // action. + // const operation_id id; - const string name; + const operation_id outer_id; + const char* name; // Name derivatives for diagnostics. Note that unlike meta-operations, // these can only be empty for the default operation (id 1), And @@ -332,10 +344,10 @@ namespace build2 // have empty derivatives (failed which only target name will be // printed). // - const string name_do; // E.g., [to] 'update'. - const string name_doing; // E.g., [while] 'updating'. - const string name_did; // E.g., [not] 'updated'. - const string name_done; // E.g., 'is up to date'. + const char* name_do; // E.g., [to] 'update'. + const char* name_doing; // E.g., [while] 'updating'. + const char* name_did; // E.g., [not] 'updated'. + const char* name_done; // E.g., 'is up to date'. const execution_mode mode; @@ -346,9 +358,8 @@ namespace build2 // The first argument in all the callback is the operation parameters. // - // If the meta-operation expects parameters, then it should have a - // non-NULL pre(). Failed that, any parameters will be diagnosed as - // unexpected. + // If the operation expects parameters, then it should have a non-NULL + // pre(). Failed that, any parameters will be diagnosed as unexpected. // If the returned operation_id's are not 0, then they are injected // as pre/post operations for this operation. Can be NULL if unused. diff --git a/build2/parser.cxx b/build2/parser.cxx index 5687b0f..0c360a5 100644 --- a/build2/parser.cxx +++ b/build2/parser.cxx @@ -4290,12 +4290,12 @@ namespace build2 if (n.pair || !n.simple () || n.empty ()) return false; - // C identifier. + // Like C identifier but with '-' instead of '_' as the delimiter. // for (size_t i (0); i != n.value.size (); ++i) { char c (n.value[i]); - if (c != '_' && !(i != 0 ? alnum (c) : alpha (c))) + if (c != '-' && !(i != 0 ? alnum (c) : alpha (c))) return false; } diff --git a/build2/test/init.cxx b/build2/test/init.cxx index 556de00..6d28e9e 100644 --- a/build2/test/init.cxx +++ b/build2/test/init.cxx @@ -29,9 +29,10 @@ namespace build2 l5 ([&]{trace << "for " << rs.out_path ();}); - // Register the test operation. + // Register our operations. // rs.operations.insert (test_id, op_test); + rs.operations.insert (update_for_test_id, op_update_for_test); // Enter module variables. Do it during boot in case they get assigned // in bootstrap.build. diff --git a/build2/test/operation.cxx b/build2/test/operation.cxx index e4591e4..7c968ff 100644 --- a/build2/test/operation.cxx +++ b/build2/test/operation.cxx @@ -24,6 +24,7 @@ namespace build2 const operation_info op_test { test_id, + 0, "test", "test", "testing", @@ -34,5 +35,21 @@ namespace build2 &test_pre, nullptr }; + + // Also the explicit update-for-test operation alias. + // + const operation_info op_update_for_test { + update_id, // Note: not update_for_test_id. + test_id, + op_update.name, + op_update.name_do, + op_update.name_doing, + op_update.name_did, + op_update.name_done, + op_update.mode, + op_update.concurrency, + op_update.pre, + op_update.post + }; } } diff --git a/build2/test/operation.hxx b/build2/test/operation.hxx index 774e8d0..09739d6 100644 --- a/build2/test/operation.hxx +++ b/build2/test/operation.hxx @@ -15,6 +15,7 @@ namespace build2 namespace test { extern const operation_info op_test; + extern const operation_info op_update_for_test; } } diff --git a/build2/test/rule.cxx b/build2/test/rule.cxx index 86062f9..a42ceca 100644 --- a/build2/test/rule.cxx +++ b/build2/test/rule.cxx @@ -303,7 +303,7 @@ namespace build2 // match_inner (a, t); - return [pass_n, this] (action a, const target& t) + return [pass_n] (action a, const target& t) { return perform_update (a, t, pass_n); }; -- cgit v1.1