From f11d720f2fb62b46ad17d3aa3850140a4839f114 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 13 Dec 2017 14:20:54 +0200 Subject: Implement info meta operation This meta operation can be used to print basic information (name, version, source/output roots, etc) for one or more projects. --- build2/b.cli | 15 ++++- build2/config/init.cxx | 4 +- build2/config/operation.cxx | 6 +- build2/config/operation.hxx | 4 +- build2/context.cxx | 1 + build2/context.hxx | 2 + build2/dist/init.cxx | 2 +- build2/dist/operation.cxx | 34 +++++------ build2/dist/operation.hxx | 2 +- build2/file.cxx | 32 ++++++++--- build2/file.hxx | 3 + build2/install/init.cxx | 4 +- build2/install/operation.cxx | 4 +- build2/install/operation.hxx | 4 +- build2/name.cxx | 3 + build2/name.hxx | 5 +- build2/operation.cxx | 133 ++++++++++++++++++++++++++++++++++++------- build2/operation.hxx | 12 ++-- build2/test/init.cxx | 2 +- build2/test/operation.cxx | 2 +- build2/test/operation.hxx | 2 +- build2/variable.cxx | 4 ++ build2/variable.hxx | 12 ++++ build2/variable.txx | 6 ++ 24 files changed, 227 insertions(+), 71 deletions(-) diff --git a/build2/b.cli b/build2/b.cli index 8148702..724a676 100644 --- a/build2/b.cli +++ b/build2/b.cli @@ -276,7 +276,20 @@ namespace build2 \li|\cb{dist} Prepare a distribution containing all files necessary to perform all - operations in a project. Implemented by the \cb{dist} module.|| + operations in a project. Implemented by the \cb{dist} module.| + + \li|\cb{info} + + Print basic information (name, version, source and output + directories, etc) about one or more projects to \cb{STDOUT}, + separating multiple projects with a blank line. Each project is + identified by its root directory target. For example: + + \ + b info: libfoo/ libbar/ + \ + + || The build system has the following built-in and pre-defined operations: diff --git a/build2/config/init.cxx b/build2/config/init.cxx index 040aeca..7285671 100644 --- a/build2/config/init.cxx +++ b/build2/config/init.cxx @@ -53,8 +53,8 @@ namespace build2 // Register meta-operations. Note that we don't register create_id // since it will be pre-processed into configure. // - rs.meta_operations.insert (configure_id, configure); - rs.meta_operations.insert (disfigure_id, disfigure); + rs.meta_operations.insert (configure_id, mo_configure); + rs.meta_operations.insert (disfigure_id, mo_disfigure); auto& vp (var_pool.rw (rs)); diff --git a/build2/config/operation.cxx b/build2/config/operation.cxx index 2fad6b0..64919f8 100644 --- a/build2/config/operation.cxx +++ b/build2/config/operation.cxx @@ -395,7 +395,7 @@ namespace build2 } } - const meta_operation_info configure { + const meta_operation_info mo_configure { configure_id, "configure", "configure", @@ -624,7 +624,7 @@ namespace build2 } } - const meta_operation_info disfigure { + const meta_operation_info mo_disfigure { disfigure_id, "disfigure", "disfigure", @@ -807,7 +807,7 @@ namespace build2 } params.clear (); - return configure.name; + return mo_configure.name; } } } diff --git a/build2/config/operation.hxx b/build2/config/operation.hxx index bf9403f..1317d38 100644 --- a/build2/config/operation.hxx +++ b/build2/config/operation.hxx @@ -14,8 +14,8 @@ namespace build2 { namespace config { - extern const meta_operation_info configure; - extern const meta_operation_info disfigure; + extern const meta_operation_info mo_configure; + extern const meta_operation_info mo_disfigure; const string& preprocess_create (const variable_overrides&, diff --git a/build2/context.cxx b/build2/context.cxx index b12920c..c4688be 100644 --- a/build2/context.cxx +++ b/build2/context.cxx @@ -386,6 +386,7 @@ namespace build2 meta_operation_table.insert ( meta_operation_data ("create", &config::preprocess_create)); meta_operation_table.insert ("dist"); + meta_operation_table.insert ("info"); operation_table.clear (); operation_table.insert ("default"); diff --git a/build2/context.hxx b/build2/context.hxx index fed6bf1..d2daad6 100644 --- a/build2/context.hxx +++ b/build2/context.hxx @@ -258,6 +258,8 @@ namespace build2 // Cached variables. // + // Note: consider printing in info meta-operation if adding anything here. + // extern const variable* var_src_root; extern const variable* var_out_root; extern const variable* var_src_base; diff --git a/build2/dist/init.cxx b/build2/dist/init.cxx index d002868..33ddcd7 100644 --- a/build2/dist/init.cxx +++ b/build2/dist/init.cxx @@ -32,7 +32,7 @@ namespace build2 // Register meta-operation. // - rs.meta_operations.insert (dist_id, dist); + rs.meta_operations.insert (dist_id, mo_dist); // Enter module variables. Do it during boot in case they get assigned // in bootstrap.build (which is customary for, e.g., dist.package). diff --git a/build2/dist/operation.cxx b/build2/dist/operation.cxx index 6692156..d63e483 100644 --- a/build2/dist/operation.cxx +++ b/build2/dist/operation.cxx @@ -54,12 +54,6 @@ namespace build2 } static void - dist_match (const values&, action, action_targets&) - { - // Don't match anything -- see execute (). - } - - static void dist_execute (const values&, action, action_targets& ts, bool) { tracer trace ("dist_execute"); @@ -249,8 +243,8 @@ namespace build2 // Make sure what we need to distribute is up to date. // { - if (perform.meta_operation_pre != nullptr) - perform.meta_operation_pre (params, loc); + if (mo_perform.meta_operation_pre != nullptr) + mo_perform.meta_operation_pre (params, loc); // This is a hack since according to the rules we need to completely // reset the state. We could have done that (i.e., saved target names @@ -259,24 +253,24 @@ namespace build2 // the dist mete-opreation is "compatible" with perform). // size_t on (current_on); - set_current_mif (perform); + set_current_mif (mo_perform); current_on = on + 1; - if (perform.operation_pre != nullptr) - perform.operation_pre (params, update_id); + if (mo_perform.operation_pre != nullptr) + mo_perform.operation_pre (params, update_id); - set_current_oif (update); + set_current_oif (op_update); action a (perform_id, update_id); - perform.match (params, a, files); - perform.execute (params, a, files, true); // Run quiet. + mo_perform.match (params, a, files); + mo_perform.execute (params, a, files, true); // Run quiet. - if (perform.operation_post != nullptr) - perform.operation_post (params, update_id); + if (mo_perform.operation_post != nullptr) + mo_perform.operation_post (params, update_id); - if (perform.meta_operation_post != nullptr) - perform.meta_operation_post (params); + if (mo_perform.meta_operation_post != nullptr) + mo_perform.meta_operation_post (params); } dir_path td (dist_root / dir_path (dist_package)); @@ -525,7 +519,7 @@ namespace build2 } } - const meta_operation_info dist { + const meta_operation_info mo_dist { dist_id, "dist", "distribute", @@ -536,7 +530,7 @@ namespace build2 &dist_operation_pre, &load, // normal load &search, // normal search - &dist_match, + nullptr, // no match (see execute()). &dist_execute, nullptr, // operation post nullptr // meta-operation post diff --git a/build2/dist/operation.hxx b/build2/dist/operation.hxx index c72e467..0639688 100644 --- a/build2/dist/operation.hxx +++ b/build2/dist/operation.hxx @@ -14,7 +14,7 @@ namespace build2 { namespace dist { - extern const meta_operation_info dist; + extern const meta_operation_info mo_dist; } } diff --git a/build2/file.cxx b/build2/file.cxx index 1ad3500..edd3e28 100644 --- a/build2/file.cxx +++ b/build2/file.cxx @@ -38,6 +38,23 @@ namespace build2 // const path config_file (build_dir / "config.build"); + ostream& + operator<< (ostream& os, const subprojects& sps) + { + for (auto b (sps.begin ()), i (b); os && i != sps.end (); ++i) + { + // See find_subprojects() for details. + // + const string& n (path::traits::is_separator (i->first.back ()) + ? empty_string + : i->first); + + os << (i != b ? " " : "") << n << '@' << i->second; + } + + return os; + } + bool is_src_root (const dir_path& d) { @@ -151,12 +168,13 @@ namespace build2 // if (first) { - rs.meta_operations.insert (noop_id, noop); - rs.meta_operations.insert (perform_id, perform); + rs.meta_operations.insert (noop_id, mo_noop); + rs.meta_operations.insert (perform_id, mo_perform); + rs.meta_operations.insert (info_id, mo_info); - rs.operations.insert (default_id, default_); - rs.operations.insert (update_id, update); - rs.operations.insert (clean_id, clean); + rs.operations.insert (default_id, op_default); + rs.operations.insert (update_id, op_update); + rs.operations.insert (clean_id, op_clean); } // If this is already a root scope, verify that things are @@ -486,10 +504,10 @@ namespace build2 // 'project' variable stays empty, here we come up with a surrogate // name for a key. The idea is that such a key should never conflict // with a real project name. We ensure this by using the project's - // sub-directory and appending trailing '/' to it. + // sub-directory and appending a trailing directory separator to it. // if (name.empty ()) - name = dir.posix_string () + '/'; + name = dir.posix_string () + path::traits::directory_separator; // @@ Can't use move() because we may need the values in diagnostics // below. Looks like C++17 try_emplace() is what we need. diff --git a/build2/file.hxx b/build2/file.hxx index d946c88..f334d8c 100644 --- a/build2/file.hxx +++ b/build2/file.hxx @@ -21,6 +21,9 @@ namespace build2 using subprojects = std::map; + ostream& + operator<< (ostream&, const subprojects&); // Print as name@dir sequence. + extern const dir_path build_dir; // build extern const dir_path bootstrap_dir; // build/bootstrap diff --git a/build2/install/init.cxx b/build2/install/init.cxx index b5fd007..3e4e1ff 100644 --- a/build2/install/init.cxx +++ b/build2/install/init.cxx @@ -142,8 +142,8 @@ namespace build2 // Register the install and uninstall operations. // - r.operations.insert (install_id, install); - r.operations.insert (uninstall_id, uninstall); + r.operations.insert (install_id, op_install); + r.operations.insert (uninstall_id, op_uninstall); } static const path cmd ("install"); diff --git a/build2/install/operation.cxx b/build2/install/operation.cxx index 5405c63..5279b3d 100644 --- a/build2/install/operation.cxx +++ b/build2/install/operation.cxx @@ -28,7 +28,7 @@ namespace build2 // those things get racy. Also, since all we do here is creating/removing // files, there is not going to be much speedup from doing it in parallel. - const operation_info install { + const operation_info op_install { install_id, "install", "install", @@ -50,7 +50,7 @@ namespace build2 // link.exe only creates a DLL's import library if there are any exported // symbols). // - const operation_info uninstall { + const operation_info op_uninstall { uninstall_id, "uninstall", "uninstall", diff --git a/build2/install/operation.hxx b/build2/install/operation.hxx index 1529621..0d96faf 100644 --- a/build2/install/operation.hxx +++ b/build2/install/operation.hxx @@ -14,8 +14,8 @@ namespace build2 { namespace install { - extern const operation_info install; - extern const operation_info uninstall; + extern const operation_info op_install; + extern const operation_info op_uninstall; } } diff --git a/build2/name.cxx b/build2/name.cxx index fe2955a..d1f3bd7 100644 --- a/build2/name.cxx +++ b/build2/name.cxx @@ -12,6 +12,9 @@ namespace build2 { + const name empty_name; + const names empty_names; + string to_string (const name& n) { diff --git a/build2/name.hxx b/build2/name.hxx index 6f329ef..4394236 100644 --- a/build2/name.hxx +++ b/build2/name.hxx @@ -90,6 +90,8 @@ namespace build2 compare (const name&) const; }; + extern const name empty_name; + inline bool operator== (const name& x, const name& y) {return x.compare (y) == 0;} @@ -140,9 +142,10 @@ namespace build2 // (names) and typed ones (vector). // using names = small_vector; - using names_view = vector_view; + extern const names empty_names; + // The same semantics as to_stream(name). // ostream& diff --git a/build2/operation.cxx b/build2/operation.cxx index eb96559..d917f1e 100644 --- a/build2/operation.cxx +++ b/build2/operation.cxx @@ -4,6 +4,8 @@ #include +#include // cout + #include #include #include @@ -41,6 +43,25 @@ namespace build2 return os; } + // noop + // + const meta_operation_info mo_noop { + noop_id, + "noop", + "", // Presumably we will never need these since we are not going + "", // to do anything. + "", + "", + nullptr, // meta-operation pre + nullptr, // operation pre + &load, + nullptr, // search + nullptr, // match + nullptr, // execute + nullptr, // operation post + nullptr // meta-operation post + }; + // perform // void @@ -395,43 +416,117 @@ namespace build2 assert (dependency_count.load (memory_order_relaxed) == 0); } - const meta_operation_info noop { - noop_id, - "noop", - "", // Presumably we will never need these since we are not going - "", // to do anything. + const meta_operation_info mo_perform { + perform_id, + "perform", + "", + "", "", "", nullptr, // meta-operation pre nullptr, // operation pre &load, - nullptr, // search - nullptr, // match - nullptr, // execute + &search, + &match, + &execute, nullptr, // operation post nullptr // meta-operation post }; - const meta_operation_info perform { - perform_id, - "perform", + // info + // + static operation_id + info_operation_pre (const values&, operation_id o) + { + if (o != default_id) + fail << "explicit operation specified for meta-operation info"; + + return o; + } + + void + info_load (const values&, + scope& rs, + const path&, + const dir_path& out_base, + const dir_path& src_base, + const location& l) + { + // For info we don't want to go any further than bootstrap so that it can + // be used in pretty much any situation (unresolved imports, etc). We do + // need to setup root as base though. + + if (rs.out_path () != out_base || rs.src_path () != src_base) + fail (l) << "meta-operation info target must be project root directory"; + + setup_base (scopes.rw (rs).insert (out_base, false), out_base, src_base); + } + + void + info_search (const values&, + const scope& rs, + const scope&, + const target_key& tk, + const location& l, + action_targets& ts) + { + // Collect all the projects we need to print information about. + + // We've already verified the target is in the project root. Now verify + // it is dir{}. + // + if (!tk.type->is_a ()) + fail (l) << "meta-operation info target must be project root directory"; + + ts.push_back (&rs); + } + + static void + info_execute (const values&, action, action_targets& ts, bool) + { + for (size_t i (0); i != ts.size (); ++i) + { + // Separate projects with blank lines. + // + if (i != 0) + cout << endl; + + const scope& s (*static_cast (ts[i])); + + // This could be a simple project that doesn't set project name. + // + cout + << "project: " << cast_empty (s[var_project]) << endl + << "version: " << cast_empty (s[var_version]) << endl + << "summary: " << cast_empty (s[var_project_summary]) << endl + << "url: " << cast_empty (s[var_project_url]) << endl + << "src_root: " << cast (s[var_src_root]) << endl + << "out_root: " << cast (s[var_out_root]) << endl + << "amalgamation: " << cast_empty (s[var_amalgamation]) << endl + << "subprojects: " << cast_empty (s[var_subprojects]) << endl; + } + } + + const meta_operation_info mo_info { + info_id, + "info", "", "", "", "", nullptr, // meta-operation pre - nullptr, // operation pre - &load, - &search, - &match, - &execute, + &info_operation_pre, + &info_load, + &info_search, + nullptr, // match + &info_execute, nullptr, // operation post nullptr // meta-operation post }; // operations // - const operation_info default_ { + const operation_info op_default { default_id, "", "", @@ -444,7 +539,7 @@ namespace build2 nullptr }; - const operation_info update { + const operation_info op_update { update_id, "update", "update", @@ -457,7 +552,7 @@ namespace build2 nullptr }; - const operation_info clean { + const operation_info op_clean { clean_id, "clean", "clean", diff --git a/build2/operation.hxx b/build2/operation.hxx index 24340dd..abd85c0 100644 --- a/build2/operation.hxx +++ b/build2/operation.hxx @@ -111,6 +111,7 @@ namespace build2 const meta_operation_id disfigure_id = 4; const meta_operation_id create_id = 5; const meta_operation_id dist_id = 6; + const meta_operation_id info_id = 7; // The default operation is a special marker that can be used to indicate // that no operation was explicitly specified by the user. If adding @@ -279,8 +280,9 @@ namespace build2 void execute (const values&, action, const action_targets&, bool quiet); - extern const meta_operation_info noop; - extern const meta_operation_info perform; + extern const meta_operation_info mo_noop; + extern const meta_operation_info mo_perform; + extern const meta_operation_info mo_info; // Operation info. // @@ -323,9 +325,9 @@ namespace build2 // Built-in operations. // - extern const operation_info default_; - extern const operation_info update; - extern const operation_info clean; + extern const operation_info op_default; + extern const operation_info op_update; + extern const operation_info op_clean; // Global meta/operation tables. Each registered meta/operation // is assigned an id which is used as an index in the per-project diff --git a/build2/test/init.cxx b/build2/test/init.cxx index 93ca1e4..1c7a2b4 100644 --- a/build2/test/init.cxx +++ b/build2/test/init.cxx @@ -31,7 +31,7 @@ namespace build2 // Register the test operation. // - rs.operations.insert (test_id, test); + rs.operations.insert (test_id, op_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 46b9366..e4591e4 100644 --- a/build2/test/operation.cxx +++ b/build2/test/operation.cxx @@ -22,7 +22,7 @@ namespace build2 return mo != disfigure_id ? update_id : 0; } - const operation_info test { + const operation_info op_test { test_id, "test", "test", diff --git a/build2/test/operation.hxx b/build2/test/operation.hxx index d834edf..774e8d0 100644 --- a/build2/test/operation.hxx +++ b/build2/test/operation.hxx @@ -14,7 +14,7 @@ namespace build2 { namespace test { - extern const operation_info test; + extern const operation_info op_test; } } diff --git a/build2/variable.cxx b/build2/variable.cxx index ed9527e..d8b48d0 100644 --- a/build2/variable.cxx +++ b/build2/variable.cxx @@ -406,6 +406,10 @@ namespace build2 throw invalid_argument (m); } + // names + // + const names& value_traits::empty_instance = empty_names; + // bool value // bool value_traits:: diff --git a/build2/variable.hxx b/build2/variable.hxx index bba2208..51cc350 100644 --- a/build2/variable.hxx +++ b/build2/variable.hxx @@ -546,6 +546,14 @@ namespace build2 static int simple_compare (const value&, const value&); + // names + // + template <> + struct value_traits + { + static const names& empty_instance; + }; + // bool // template <> @@ -783,6 +791,8 @@ namespace build2 static void prepend (value&, vector&&); static bool empty (const vector& x) {return x.empty ();} + static const vector empty_instance; + // Make sure these are static-initialized together. Failed that VC will // make sure it's done in the wrong order. // @@ -809,6 +819,8 @@ namespace build2 return append (v, move (x));} static bool empty (const map& x) {return x.empty ();} + static const map empty_instance; + // Make sure these are static-initialized together. Failed that VC will // make sure it's done in the wrong order. // diff --git a/build2/variable.txx b/build2/variable.txx index e374f22..c2dbea9 100644 --- a/build2/variable.txx +++ b/build2/variable.txx @@ -389,6 +389,9 @@ namespace build2 } template + const vector value_traits>::empty_instance; + + template const typename value_traits>::value_type_ex value_traits>::value_type = build2::value_type // VC14 wants =. { @@ -558,6 +561,9 @@ namespace build2 } template + const std::map value_traits>::empty_instance; + + template const typename value_traits>::value_type_ex value_traits>::value_type = build2::value_type // VC14 wants = { -- cgit v1.1