From 78be8e1604f183b28527159047bab69a5cbe9232 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 14 Jan 2019 22:00:36 +0300 Subject: Add b_info() that runs `b info` command and parses and returns build2 project info --- libbutl/b.cxx | 287 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 libbutl/b.cxx (limited to 'libbutl/b.cxx') diff --git a/libbutl/b.cxx b/libbutl/b.cxx new file mode 100644 index 0000000..bcf8d05 --- /dev/null +++ b/libbutl/b.cxx @@ -0,0 +1,287 @@ +// file : libbutl/b.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef __cpp_modules +#include +#endif + +// C includes. + +#include + +#ifndef __cpp_lib_modules +#include +#include +#include +#include +#include +#include + +#include // ios::failure +#include // move() +#include +#endif + +// Other includes. + +#ifdef __cpp_modules +module butl.b; + +// Only imports additional to interface. +#ifdef __clang__ +#ifdef __cpp_lib_modules +import std.core; +import std.io; +#endif +import butl.url; +import butl.path; +import butl.process; +import butl.optional; +import butl.project_name; +import butl.standard_version; +#endif + +import butl.utility; // next_word(), eof(), etc +import butl.path_io; +import butl.fdstream; +import butl.process_io; // operator<<(ostream, process_path) +import butl.small_vector; +#else +#include +#include +#include +#include +#include +#endif + +using namespace std; + +namespace butl +{ + b_error:: + b_error (const string& d, optional e) + : runtime_error (d), + exit (move (e)) + { + } + + [[noreturn]] static inline void + bad_value (const string& d) + { + throw runtime_error ("invalid " + d); + } + + b_project_info + b_info (const dir_path& project, + uint16_t verb, + const function& cmd_callback, + const path& program, + const dir_path& search_fallback, + const vector& ops) + { + try + { + process_path pp ( + process::path_search (program, true /* init */, search_fallback)); + + process pr; + try + { + fdpipe pipe (fdopen_pipe ()); // Text mode seems appropriate. + + small_vector vops; + + // Let's suppress warnings the `b info` command may issue, unless the + // verbosity level is 2 (-v) or higher. For example: + // + // warning: configured src_root prj/ does not match forwarded prj/prj/ + // + if (verb >= 2) + { + vops.push_back ("--verbose"); + vops.push_back (std::to_string (verb)); + } + else + vops.push_back ("-q"); + + pr = process_start_callback ( + cmd_callback ? cmd_callback : [] (const char* const*, size_t) {}, + 0 /* stdin */, + pipe, + 2 /* stderr */, + pp, + vops, + ops, + "info:", project.representation ()); + + pipe.out.close (); + ifdstream is (move (pipe.in), fdstream_mode::skip, ifdstream::badbit); + + auto parse_name = [] (string&& s, const char* what) + { + try + { + return project_name (move (s)); + } + catch (const invalid_argument& e) + { + bad_value (string (what) + " name '" + s + "': " + e.what ()); + } + }; + + auto parse_dir = [] (string&& s, const char* what) + { + try + { + return dir_path (move (s)); + } + catch (const invalid_path& e) + { + bad_value ( + string (what) + " directory '" + e.path + "': " + e.what ()); + } + }; + + b_project_info r; + for (string l; !eof (getline (is, l)); ) + { + if (l.compare (0, 9, "project: ") == 0) + { + string v (l, 9); + if (!v.empty ()) + r.project = parse_name (move (v), "project"); + } + else if (l.compare (0, 9, "version: ") == 0) + { + string v (l, 9); + if (!v.empty ()) + try + { + r.version = standard_version (v); + } + catch (const invalid_argument& e) + { + bad_value ("version '" + v + "': " + e.what ()); + } + } + else if (l.compare (0, 9, "summary: ") == 0) + { + r.summary = string (l, 9); + } + else if (l.compare (0, 5, "url: ") == 0) + { + string v (l, 5); + if (!v.empty ()) + try + { + r.url = url (v); + } + catch (const invalid_argument& e) + { + bad_value ("url '" + v + "': " + e.what ()); + } + } + else if (l.compare (0, 10, "src_root: ") == 0) + { + r.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"); + } + else if (l.compare (0, 14, "amalgamation: ") == 0) + { + string v (l, 14); + if (!v.empty ()) + r.amalgamation = parse_dir (move (v), "amalgamation"); + } + else if (l.compare (0, 13, "subprojects: ") == 0) + { + string v (l, 13); + + for (size_t b (0), e (0); next_word (v, b, e); ) + { + string s (v, b, e - b); + size_t p (s.find ('@')); + + if (p == string::npos) + bad_value ("subproject '" + s + "': missing '@'"); + + project_name sn; + if (p != 0) + sn = parse_name (string (s, 0, p), "subproject"); + + r.subprojects.push_back ( + b_project_info::subproject {move (sn), + parse_dir (string (s, p + 1), + "subproject")}); + } + } + else if (l.compare (0, 12, "operations: ") == 0) + { + 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)); + } + 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)); + } + } + + is.close (); // Detect errors. + + if (pr.wait ()) + return r; + } + // Note that ios::failure inherits from std::runtime_error, so this + // catch-clause must go last. + // + catch (const ios::failure& e) + { + // Note: using ostream to get sanitized representation. + // + if (pr.wait ()) + { + ostringstream os; + os << "error reading " << pp << " output: " << e; + throw b_error (os.str (), move (pr.exit)); + } + else if (!pr.exit) // Pipe opening failed before the process started. + { + ostringstream os; + os << "unable to open pipe: " << e; + throw b_error (os.str ()); + } + + // Fall through. + } + catch (const runtime_error& e) + { + // Re-throw as b_error if the process exited normally with zero code. + // + if (pr.wait ()) + throw b_error (e.what (), move (pr.exit)); + + // Fall through. + } + + // We should only get here if the child exited with an error status. + // + assert (!pr.wait ()); + + throw b_error ( + string ("process ") + pp.recall_string () + " " + to_string (*pr.exit), + move (pr.exit)); + } + catch (const process_error& e) + { + ostringstream os; + os << "unable to execute " << program << ": " << e; + throw b_error (os.str ()); + } + } +} -- cgit v1.1