diff options
author | Karen Arutyunov <karen@codesynthesis.com> | 2019-03-18 13:19:12 +0300 |
---|---|---|
committer | Karen Arutyunov <karen@codesynthesis.com> | 2019-03-23 15:40:19 +0300 |
commit | 58f0d15c5da74f7908e57ef60ceb5c3d0a7319e3 (patch) | |
tree | ccf9a01aec53f2c1396c6ec85fc6a3186af22260 /tests/command/driver.cxx | |
parent | ede5f2102b2047a75476d3f5db81dac572196aa6 (diff) |
Add command running API
Diffstat (limited to 'tests/command/driver.cxx')
-rw-r--r-- | tests/command/driver.cxx | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/tests/command/driver.cxx b/tests/command/driver.cxx new file mode 100644 index 0000000..a1baf68 --- /dev/null +++ b/tests/command/driver.cxx @@ -0,0 +1,255 @@ +// file : tests/command/driver.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <cassert> + +#ifndef __cpp_lib_modules +#include <ios> +#include <string> +#include <vector> +#include <iostream> +#include <stdexcept> // invalid_argument +#endif + +// Other includes. + +#ifdef __cpp_modules +#ifdef __cpp_lib_modules +import std.core; +import std.io; +#endif +import butl.path; +import butl.path_io; +import butl.process; // process::print() +import butl.command; +import butl.utility; +import butl.optional; +#else +#include <libbutl/path.mxx> +#include <libbutl/path-io.mxx> +#include <libbutl/process.mxx> +#include <libbutl/command.mxx> +#include <libbutl/utility.mxx> +#include <libbutl/optional.mxx> +#endif + +using namespace std; +using namespace butl; + +// Usages: +// +// argv[0] [-d <dir>] [-v <name>[=<value>]] [-s <name>=<value>] [-c <char>] +// [-p] <command> +// +// argv[0] -C [-A] [-D] [-V <name>] [-S <status>] <arguments> +// +// In the first form run the specified command, changing the current +// directory, (re)setting the environment variables, performing substitutions, +// and printing the "expanded" command line, if requested. +// +// In the second form optionally print the program arguments, CWD, the +// environment variable values and exit with the status specified. This mode +// is normally used for the command being tested to dump the environment +// obtained from the caller. +// +// -d <dir> +// Change the CWD for the command process. +// +// -v <name>[=<value>] +// (Un)set the environment variable for the command process. Can be +// specified multiple times. +// +// -s <name>=<value> +// Perform command line substitutions using the specified variable. Can be +// specified multiple times. +// +// -c <char> +// Substitution symbol. The default is '@'. +// +// -p +// Print the "expanded" command line. +// +// -C +// Print the program arguments (-A), CWD (-D), environment variables (-V) +// to stdout and exit with the status specifies (-S). +// +// -A +// Print the program arguments to stdout. +// +// -D +// Print the process CWD to stdout. +// +// -V <name> +// Print the environment variable value (or <unset>) to stdout. Can be +// specified multiple times. +// +// -S <status> +// Exit with the specified status code. +// +int +main (int argc, const char* argv[]) +{ + using butl::optional; + using butl::getenv; + + // Parse and validate the arguments. + // + dir_path cwd; + vector<const char*> vars; + optional<command_substitution_map> substitutions; + char subst ('@'); + optional<string> command; + bool print (false); + + for (int i (1); i != argc; ++i) + { + string o (argv[i]); + + if (o == "-d") + { + assert (++i != argc); + cwd = dir_path (argv[i]); + } + else if (o == "-v") + { + assert (++i != argc); + vars.push_back (argv[i]); + } + else if (o == "-s") + { + assert (++i != argc); + + if (!substitutions) + substitutions = command_substitution_map (); + + string v (argv[i]); + size_t p (v.find ('=')); + + assert (p != string::npos && p != 0); + + (*substitutions)[string (v, 0, p)] = string (v, p + 1); + } + else if (o == "-c") + { + assert (++i != argc); + + string v (argv[i]); + + assert (v.size () == 1); + subst = v[0]; + } + else if (o == "-p") + { + print = true; + } + else if (o == "-C") + { + assert (i == 1); // Must go first. + + int ec (0); + bool print_cwd (false); + + // Include the program path into the arguments list. + // + vector<const char*> args ({argv[0]}); + + for (++i; i != argc; ++i) + { + o = argv[i]; + + if (o == "-A") + { + print = true; + } + else if (o == "-D") + { + print_cwd = true; + } + else if (o == "-V") + { + assert (++i != argc); + vars.push_back (argv[i]); + } + else if (o == "-S") + { + assert (++i != argc); + ec = stoi (argv[i]); + } + else + args.push_back (argv[i]); + } + + args.push_back (nullptr); + + if (print) + { + process::print (cout, args.data ()); + cout << endl; + } + + if (print_cwd) + cout << dir_path::current_directory () << endl; + + for (const auto& v: vars) + { + optional<string> vv (getenv (v)); + cout << (vv ? *vv : "<unset>") << endl; + } + + return ec; + } + else + { + assert (!command); + command = argv[i]; + } + } + + assert (command); + + // Run the command. + // + try + { + optional<process_env> pe; + + if (!cwd.empty () || !vars.empty ()) + pe = process_env (process_path (), cwd, vars); + + process_exit e (command_run (*command, + pe, + substitutions, + subst, + [print] (const char* const args[], size_t n) + { + if (print) + { + process::print (cout, args, n); + cout << endl; + } + })); + + if (!e) + cerr << "process " << argv[0] << " " << e << endl; + + return e.normal () ? e.code () : 1; + } + catch (const invalid_argument& e) + { + cerr << e << endl; + return 1; + } + catch (const ios::failure& e) + { + cerr << e << endl; + return 1; + } + catch (const process_error& e) + { + cerr << e << endl; + return 1; + } + + return 0; +} |