utilities.cxx
Go to the documentation of this file.
1 // ============================================================================
2 // Copyright (c) 2011-2012 University of Pennsylvania
3 // Copyright (c) 2013-2016 Andreas Schuh
4 // All rights reserved.
5 //
6 // See COPYING file for license information or visit
7 // https://cmake-basis.github.io/download.html#license
8 // ============================================================================
9 
10 /**
11  * @file utilities.cxx
12  * @brief Main module of project-independent BASIS utilities.
13  *
14  * @ingroup BasisCxxUtilities
15  */
16 
17 #include <basis/subprocess.h>
18 #include <basis/utilities.h>
19 
20 
21 // acceptable in .cxx file
22 using namespace std;
23 
24 
25 namespace basis { namespace util {
26 
27 
28 // ===========================================================================
29 // executable information
30 // ===========================================================================
31 
32 // ---------------------------------------------------------------------------
33 void print_contact(const char* contact)
34 {
35  cout << "Contact:\n " << contact << endl;
36 }
37 
38 // ---------------------------------------------------------------------------
39 void print_version(const char* name, const char* version, const char* project,
40  const char* copyright, const char* license)
41 {
42  assert(name != NULL);
43  assert(version != NULL);
44  cout << name;
45  if (project && *project) cout << " (" << project << ")";
46  cout << " " << version << endl;
47  if (copyright && *copyright) cout << "Copyright (c) " << copyright << ". All rights reserved." << endl;
48  if (license && *license) cout << license << endl;
49 }
50 
51 // ---------------------------------------------------------------------------
52 string targetuid(const string& name, const IExecutableTargetInfo* targets)
53 {
54  return targets != NULL ? targets->targetuid(name) : "";
55 }
56 
57 // ---------------------------------------------------------------------------
58 bool istarget(const string& name, const IExecutableTargetInfo* targets)
59 {
60  return targets != NULL && targets->istarget(name);
61 }
62 
63 // ---------------------------------------------------------------------------
64 string exepath(const string& name, const IExecutableTargetInfo* targets)
65 {
66  // return path of this executable if no name given
67  if (name.empty()) return os::exepath();
68  // get name of executable and check if target name is known
69  string exec_name = targets != NULL ? targets->basename(name) : "";
70  // if target is not known
71  if (exec_name.empty()) {
72  // return input argument assuming that it is already the path of
73  // an executable file
74  exec_name = name;
75  // try to get absolute path using the which command if path is relative
76  // TODO Replace use of external which command by C++ implementation
77  // of which() even though BASIS includes a Python implementation
78  // that can also be used on Windows. Still, a native C++
79  // implementation is desireable.
80  if (!os::path::isabs(exec_name) && targets->istarget("basis.which")) {
81  vector<string> which(2);
82  which[0] = "basis.which";
83  which[1] = name;
84  ostringstream oss;
85  // attention: this includes a "recursive" call of this function!
86  if (execute(which, true, &oss, true, 0, false, targets) == 0) {
87  exec_name = oss.str();
88  const string::size_type end = exec_name.find_last_not_of(" \t\n\r");
89  if (end == string::npos) {
90  exec_name = "";
91  } else {
92  exec_name.erase(end + 1);
93  }
94  }
95  }
96  return exec_name;
97  }
98  return os::path::join(targets->dirname(name), exec_name);
99 }
100 
101 // ---------------------------------------------------------------------------
102 string exename(const std::string& name, const IExecutableTargetInfo* targets)
103 {
104  string exec_path = exepath(name, targets);
105  if (exec_path.empty()) return "";
106 #if WINDOWS
107  string fname, ext;
108  os::path::splitext(exec_path, fname, ext);
109  if (ext == ".exe" || ext == ".com") exec_path = fname;
110 #endif
111  return os::path::basename(exec_path);
112 }
113 
114 // ---------------------------------------------------------------------------
115 string exedir(const std::string& name, const IExecutableTargetInfo* targets)
116 {
117  string exec_path = exepath(name, targets);
118  return exec_path.empty() ? "" : os::path::dirname(exec_path);
119 }
120 
121 // ===========================================================================
122 // command execution
123 // ===========================================================================
124 
125 // ---------------------------------------------------------------------------
126 string tostring(const vector<string>& args)
127 {
128  return Subprocess::tostring(args);
129 }
130 
131 // ---------------------------------------------------------------------------
132 vector<string> qsplit(const string& args)
133 {
134  return Subprocess::split(args);
135 }
136 
137 // ---------------------------------------------------------------------------
138 int execute(const string& cmd, bool quiet, ostream* out,
139  bool allow_fail, int verbose, bool simulate,
140  const IExecutableTargetInfo* targets)
141 {
142  vector<string> args = Subprocess::split(cmd);
143  return execute(args, quiet, out, allow_fail, verbose, simulate, targets);
144 }
145 
146 // ---------------------------------------------------------------------------
147 int execute(vector<string> args, bool quiet, ostream* out,
148  bool allow_fail, int verbose, bool simulate,
149  const IExecutableTargetInfo* targets)
150 {
151  if (args.empty() || args[0].empty()) {
152  BASIS_THROW(SubprocessError, "execute_process(): No command specified");
153  }
154  // map build target name to executable file path
155  string exec_path = exepath(args[0], targets);
156  // prepend absolute path of found executable
157  if (!exec_path.empty()) args[0] = exec_path;
158  // some verbose output
159  if (verbose > 0 || simulate) {
160  cout << "$ " << Subprocess::tostring(args);
161  if (simulate) cout << " (simulated)";
162  cout << endl;
163  }
164  // execute command
165  char buf[1024];
166  int n;
167  int status = 0;
168  Subprocess p;
169  if (!p.popen(args, Subprocess::RM_NONE, Subprocess::RM_PIPE, Subprocess::RM_PIPE)) {
170  BASIS_THROW(SubprocessError, "execute_process(): Failed to create subprocess");
171  }
172  // read child's stdout (blocking)
173  if (!quiet || out != NULL) {
174  while ((n = p.read(buf, 1023)) > 0) {
175  buf[n] = '\0';
176  if (!quiet) {
177  cout << buf;
178  cout.flush();
179  }
180  if (out) *out << buf;
181  }
182  }
183  // wait for child process
184  if (!p.wait()) {
185  BASIS_THROW(SubprocessError, "execute_process(): Failed to wait for subprocess");
186  }
187  // write error messages to stderr of parent process
188  while ((n = p.read(buf, 1023, true)) > 0) {
189  buf[n] = '\0';
190  cerr << buf;
191  }
192  // get exit code
193  status = p.returncode();
194  // if command failed, throw an exception
195  if (status != 0 && !allow_fail) {
196  BASIS_THROW(SubprocessError, "Command " << Subprocess::tostring(args) << " failed");
197  }
198  return status;
199 }
200 
201 
202 } } // end of namespaces
virtual bool istarget(const std::string &target) const =0
Determine whether a given build target is known.
function targetuid(out uid, in name)
Get UID of build target.
#define assert(condition)
Assertion without custom message.
Definition: assert.h:44
virtual std::string basename(const std::string &target) const =0
Get name of executable file without directory path.
Main module of project-independent BASIS utilities.
virtual std::string targetuid(const std::string &target) const =0
Get UID of build target.
function istarget(in target)
Determine whether a given build target is known.
def which(command, path=None, verbose=0, exts=None)
Definition: which.py:257
function tostring(out var, in elements)
Build quoted string from array.
std::string join(const std::string &base, const std::string &path)
Join two paths, e.g., base path and relative path.
Definition: path.cxx:432
bool popen(const CommandLine &args, const RedirectMode rm_in=RM_NONE, const RedirectMode rm_out=RM_NONE, const RedirectMode rm_err=RM_NONE, const Environment *env=NULL)
Open new subprocess.
Definition: subprocess.cxx:218
Module used to execute subprocesses.
STL namespace.
bool isabs(const std::string &path)
Test whether a given path is absolute.
Definition: path.cxx:292
Exception type thrown by execute().
Definition: utilities.h:278
Definition: basis.h:34
#define BASIS_THROW(type, msg)
Throw exception with given message.
Definition: except.h:48
std::string basename(const std::string &path)
Get file name.
Definition: path.cxx:273
Provides information about executable build targets.
Definition: utilities.h:88
int read(void *buf, size_t nbuf, bool err=false)
Read data from stdout or stderr of subprocess.
Definition: subprocess.cxx:751
void split(const std::string &path, std::string &head, std::string &tail)
Split path into two parts.
Definition: path.cxx:164
bool wait()
Wait for subprocess to terminate.
Definition: subprocess.cxx:504
void print_version(const char *name, const char *version=NULL, const char *project=NULL, const char *copyright=NULL, const char *license=NULL)
Print version information including copyright and license notices.
Definition: basis.cxx:253
Platform-independent interface to create and control a subprocess.
Definition: subprocess.h:40
function exepath(out path, in target)
Get absolute path of executable file.
void splitext(const std::string &path, std::string &head, std::string &ext, const std::set< std::string > *exts=NULL, bool icase=false)
Get file name extension.
Definition: path.cxx:212
function qsplit(out var, in str)
Split (quoted) string.
std::string dirname(const std::string &path)
Get file directory.
Definition: path.cxx:265
int returncode() const
Definition: subprocess.cxx:620
function exedir(out dir, in name)
Get directory of executable file.
MultiSwitchArg verbose("v", "verbose", "Increase verbosity of output messages.", false)
virtual std::string dirname(const std::string &target) const =0
Get absolute path to directory containing executable.
void print_contact(const char *contact=NULL)
Print contact information.
Definition: basis.cxx:247
function exename(out file, in name)
Get name of executable file.
function execute(in options, in cmd, in args)
Execute command as subprocess.