aboutsummaryrefslogtreecommitdiff
path: root/tests/command/driver.cxx
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2019-03-18 13:19:12 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2019-03-23 15:40:19 +0300
commit58f0d15c5da74f7908e57ef60ceb5c3d0a7319e3 (patch)
treeccf9a01aec53f2c1396c6ec85fc6a3186af22260 /tests/command/driver.cxx
parentede5f2102b2047a75476d3f5db81dac572196aa6 (diff)
Add command running API
Diffstat (limited to 'tests/command/driver.cxx')
-rw-r--r--tests/command/driver.cxx255
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;
+}