From acde650d74c2d7f41328fa94dd130ca144fd5487 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 16 Aug 2021 17:04:49 +0300 Subject: Add b_info() overload that returns information for multiple projects --- libbutl/b.cxx | 110 ++++++++++++++++++++++++++++++++++++++++------------------ libbutl/b.mxx | 42 ++++++++++++++++++---- 2 files changed, 113 insertions(+), 39 deletions(-) diff --git a/libbutl/b.cxx b/libbutl/b.cxx index c55496c..86a87ff 100644 --- a/libbutl/b.cxx +++ b/libbutl/b.cxx @@ -72,8 +72,9 @@ namespace butl throw runtime_error ("invalid " + d); } - b_project_info - b_info (const dir_path& project, + void + b_info (std::vector& r, + const vector& projects, bool ext_mods, uint16_t verb, const function& cmd_callback, @@ -81,6 +82,20 @@ namespace butl const dir_path& search_fallback, const vector& ops) { + // Bail out if the project list is empty. + // + if (projects.empty ()) + return; + + // Reserve enough space in the result and save its original size. + // + size_t rn (r.size ()); + { + size_t n (rn + projects.size ()); + if (r.capacity () < n) + r.reserve (n); + } + try { process_path pp ( @@ -106,6 +121,14 @@ namespace butl else vops.push_back ("-q"); + vector ps; + ps.reserve (projects.size ()); + + // Note that quoting is essential here. + // + for (const dir_path& p: projects) + ps.push_back ("'" + p.representation () + "'"); + pr = process_start_callback ( cmd_callback ? cmd_callback : [] (const char* const*, size_t) {}, 0 /* stdin */, @@ -116,7 +139,7 @@ namespace butl ext_mods ? nullptr : "--no-external-modules", "-s", ops, - "info:", "'" + project.representation () + "'"); + "info:", ps); pipe.out.close (); ifdstream is (move (pipe.in), fdstream_mode::skip, ifdstream::badbit); @@ -146,22 +169,52 @@ namespace butl } }; - b_project_info r; + b_project_info pi; + auto add_project = [&r, &pi] () + { + // Parse version string to standard version if the project loaded + // the version module. + // + const auto& ms (pi.modules); + if (find (ms.begin (), ms.end (), "version") != ms.end ()) + { + try + { + pi.version = standard_version (pi.version_string, + standard_version::allow_stub); + } + catch (const invalid_argument& e) + { + bad_value ("version '" + pi.version_string + "': " + e.what ()); + } + } + + // Add the project info and prepare for the next project info + // parsing. + // + r.push_back (move (pi)); + pi = b_project_info (); + }; + for (string l; !eof (getline (is, l)); ) { - if (l.compare (0, 9, "project: ") == 0) + if (l.empty ()) + { + add_project (); + } + else if (l.compare (0, 9, "project: ") == 0) { string v (l, 9); if (!v.empty ()) - r.project = parse_name (move (v), "project"); + pi.project = parse_name (move (v), "project"); } else if (l.compare (0, 9, "version: ") == 0) { - r.version_string = string (l, 9); + pi.version_string = string (l, 9); } else if (l.compare (0, 9, "summary: ") == 0) { - r.summary = string (l, 9); + pi.summary = string (l, 9); } else if (l.compare (0, 5, "url: ") == 0) { @@ -169,7 +222,7 @@ namespace butl if (!v.empty ()) try { - r.url = url (v); + pi.url = url (v); } catch (const invalid_argument& e) { @@ -178,17 +231,17 @@ namespace butl } else if (l.compare (0, 10, "src_root: ") == 0) { - r.src_root = parse_dir (string (l, 10), "src_root"); + pi.src_root = parse_dir (string (l, 10), "src_root"); } else if (l.compare (0, 10, "out_root: ") == 0) { - r.out_root = parse_dir (string (l, 10), "out_root"); + pi.out_root = parse_dir (string (l, 10), "out_root"); } else if (l.compare (0, 14, "amalgamation: ") == 0) { string v (l, 14); if (!v.empty ()) - r.amalgamation = parse_dir (move (v), "amalgamation"); + pi.amalgamation = parse_dir (move (v), "amalgamation"); } else if (l.compare (0, 13, "subprojects: ") == 0) { @@ -206,7 +259,7 @@ namespace butl if (p != 0) sn = parse_name (string (s, 0, p), "subproject"); - r.subprojects.push_back ( + pi.subprojects.push_back ( b_project_info::subproject {move (sn), parse_dir (string (s, p + 1), "subproject")}); @@ -216,19 +269,19 @@ namespace butl { string v (l, 12); for (size_t b (0), e (0); next_word (v, b, e); ) - r.operations.push_back (string (v, b, e - b)); + pi.operations.push_back (string (v, b, e - b)); } else if (l.compare (0, 17, "meta-operations: ") == 0) { string v (l, 17); for (size_t b (0), e (0); next_word (v, b, e); ) - r.meta_operations.push_back (string (v, b, e - b)); + pi.meta_operations.push_back (string (v, b, e - b)); } else if (l.compare (0, 9, "modules: ") == 0) { string v (l, 9); for (size_t b (0), e (0); next_word (v, b, e); ) - r.modules.push_back (string (v, b, e - b)); + pi.modules.push_back (string (v, b, e - b)); } } @@ -236,24 +289,15 @@ namespace butl if (pr.wait ()) { - // Parse version string to standard version if the project loaded - // the version module. - // - const auto& ms (r.modules); - if (find (ms.begin (), ms.end (), "version") != ms.end ()) - { - try - { - r.version = standard_version (r.version_string, - standard_version::allow_stub); - } - catch (const invalid_argument& e) - { - bad_value ("version '" + r.version_string + "': " + e.what ()); - } - } + add_project (); // Add the remaining project info. + + if (r.size () - rn == projects.size ()) + return; - return r; + ostringstream os; + os << "invalid " << pp << " output: expected information for " + << projects.size () << " projects instead of " << r.size () - rn; + throw b_error (os.str (), move (pr.exit)); } } // Note that ios::failure inherits from std::runtime_error, so this diff --git a/libbutl/b.mxx b/libbutl/b.mxx index 97dcf0c..cca9696 100644 --- a/libbutl/b.mxx +++ b/libbutl/b.mxx @@ -10,6 +10,7 @@ #ifndef __cpp_lib_modules_ts #include #include +#include // move() #include // size_tu #include // uint16_t #include // runtime_error @@ -64,8 +65,12 @@ LIBBUTL_MODEXPORT namespace butl b_error (const std::string& description, optional = nullopt); }; - // Run `b info: ` command and parse and return the build2 - // project information it prints to stdout. Throw b_error on error. + // Run `b info: ...` command and parse and return (via argument + // to allow appending and for error position; see below) the build2 projects + // information it prints to stdout. Return the empty list if the specified + // project list is empty. Throw b_error on error. Note that the size of the + // result vector can be used to determine which project information caused + // the error. // // Unless you need information that may come from external modules // (operations, meta-operations, etc), pass false as the ext_mods argument, @@ -76,8 +81,8 @@ LIBBUTL_MODEXPORT namespace butl // (see process_run_callback() for details), build program search details, // and additional options. // - // Note that version_string is only parsed to standard_version if the - // project uses the version module. Otherwise, standard_version is empty. + // Note that version_string is only parsed to standard_version if a project + // uses the version module. Otherwise, standard_version is empty. // struct b_project_info { @@ -110,12 +115,37 @@ LIBBUTL_MODEXPORT namespace butl using b_callback = void (const char* const args[], std::size_t n); - LIBBUTL_SYMEXPORT b_project_info - b_info (const dir_path& project, + LIBBUTL_SYMEXPORT void + b_info (std::vector& result, + const std::vector& projects, bool ext_mods, std::uint16_t verb = 1, const std::function& cmd_callback = {}, const path& program = path ("b"), const dir_path& search_fallback = {}, const std::vector& options = {}); + + // As above but retrieve information for a single project. + // + inline b_project_info + b_info (const dir_path& project, + bool ext_mods, + std::uint16_t verb = 1, + const std::function& cmd_callback = {}, + const path& program = path ("b"), + const dir_path& search_fallback = {}, + const std::vector& options = {}) + { + std::vector r; + b_info (r, + std::vector ({project}), + ext_mods, + verb, + cmd_callback, + program, + search_fallback, + options); + + return std::move (r[0]); + } } -- cgit v1.1