cmExecProgramCommand.cxx 7.86 KB
Newer Older
1 2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3
#include "cmExecProgramCommand.h"
Brad King's avatar
Brad King committed
4

5
#include "cmsys/Process.h"
6 7 8
#include <stdio.h>

#include "cmMakefile.h"
9
#include "cmProcessOutput.h"
10 11
#include "cmSystemTools.h"

12
class cmExecutionStatus;
13

14
// cmExecProgramCommand
15 16
bool cmExecProgramCommand::InitialPass(std::vector<std::string> const& args,
                                       cmExecutionStatus&)
17
{
18
  if (args.empty()) {
19 20
    this->SetError("called with incorrect number of arguments");
    return false;
21
  }
22 23 24
  std::string arguments;
  bool doingargs = false;
  int count = 0;
25 26 27 28
  std::string output_variable;
  bool haveoutput_variable = false;
  std::string return_variable;
  bool havereturn_variable = false;
29 30
  for (std::string const& arg : args) {
    if (arg == "OUTPUT_VARIABLE") {
31 32
      count++;
      doingargs = false;
33 34
      havereturn_variable = false;
      haveoutput_variable = true;
35 36
    } else if (haveoutput_variable) {
      if (!output_variable.empty()) {
37 38
        this->SetError("called with incorrect number of arguments");
        return false;
39
      }
40
      output_variable = arg;
41
      haveoutput_variable = false;
42
      count++;
43
    } else if (arg == "RETURN_VALUE") {
44
      count++;
45 46 47
      doingargs = false;
      haveoutput_variable = false;
      havereturn_variable = true;
48 49
    } else if (havereturn_variable) {
      if (!return_variable.empty()) {
50 51
        this->SetError("called with incorrect number of arguments");
        return false;
52
      }
53
      return_variable = arg;
54
      havereturn_variable = false;
55
      count++;
56
    } else if (arg == "ARGS") {
57
      count++;
58 59
      havereturn_variable = false;
      haveoutput_variable = false;
60
      doingargs = true;
61
    } else if (doingargs) {
62
      arguments += arg;
63 64
      arguments += " ";
      count++;
65
    }
66
  }
67 68

  std::string command;
69
  if (!arguments.empty()) {
70
    command = cmSystemTools::ConvertToRunCommandPath(args[0]);
71 72
    command += " ";
    command += arguments;
73
  } else {
74
    command = args[0];
75
  }
76
  bool verbose = true;
77
  if (!output_variable.empty()) {
78
    verbose = false;
79
  }
80
  int retVal = 0;
81
  std::string output;
82
  bool result = true;
83
  if (args.size() - count == 2) {
84
    cmSystemTools::MakeDirectory(args[1]);
85
    result = cmExecProgramCommand::RunCommand(command, output, retVal,
86
                                              args[1].c_str(), verbose);
87
  } else {
88 89
    result = cmExecProgramCommand::RunCommand(command, output, retVal, nullptr,
                                              verbose);
90 91
  }
  if (!result) {
92
    retVal = -1;
93
  }
94

95
  if (!output_variable.empty()) {
96 97
    std::string::size_type first = output.find_first_not_of(" \n\t\r");
    std::string::size_type last = output.find_last_not_of(" \n\t\r");
98
    if (first == std::string::npos) {
99
      first = 0;
100 101 102 103
    }
    if (last == std::string::npos) {
      last = output.size() - 1;
    }
104

105
    std::string coutput = std::string(output, first, last - first + 1);
106
    this->Makefile->AddDefinition(output_variable, coutput);
107
  }
108

109
  if (!return_variable.empty()) {
110 111
    char buffer[100];
    sprintf(buffer, "%d", retVal);
Stephen Kelly's avatar
Stephen Kelly committed
112
    this->Makefile->AddDefinition(return_variable, buffer);
113
  }
114

115
  return true;
116 117
}

118
bool cmExecProgramCommand::RunCommand(std::string command, std::string& output,
119
                                      int& retVal, const char* dir,
120
                                      bool verbose, Encoding encoding)
121
{
122
  if (cmSystemTools::GetRunCommandOutput()) {
123
    verbose = false;
124
  }
125

126
#if defined(_WIN32) && !defined(__CYGWIN__)
127 128 129 130
  // if the command does not start with a quote, then
  // try to find the program, and if the program can not be
  // found use system to run the command as it must be a built in
  // shell command like echo or dir
131
  if (!command.empty() && command[0] == '\"') {
132
    // count the number of quotes
133 134 135
    int count = 0;
    for (char c : command) {
      if (c == '\"') {
136
        count++;
137
        if (count > 2) {
138 139 140
          break;
        }
      }
141
    }
142 143 144 145
    // if there are more than two double quotes use
    // GetShortPathName, the cmd.exe program in windows which
    // is used by system fails to execute if there are more than
    // one set of quotes in the arguments
146
    if (count > 2) {
147
      cmsys::RegularExpression quoted("^\"([^\"]*)\"[ \t](.*)");
148
      if (quoted.find(command)) {
149
        std::string shortCmd;
150 151
        std::string cmd = quoted.match(1);
        std::string args = quoted.match(2);
152
        if (!cmSystemTools::FileExists(cmd)) {
153
          shortCmd = cmd;
154
        } else if (!cmSystemTools::GetShortPath(cmd, shortCmd)) {
155
          cmSystemTools::Error("GetShortPath failed for " + cmd);
156
          return false;
157
        }
158 159 160
        shortCmd += " ";
        shortCmd += args;

161
        command = shortCmd;
162
      } else {
163
        cmSystemTools::Error("Could not parse command line with quotes " +
164 165 166
                             command);
      }
    }
167
  }
168 169 170 171
#endif

  // Allocate a process instance.
  cmsysProcess* cp = cmsysProcess_New();
172
  if (!cp) {
173 174
    cmSystemTools::Error("Error allocating process instance.");
    return false;
175
  }
176

177
#if defined(_WIN32) && !defined(__CYGWIN__)
178
  if (dir) {
179
    cmsysProcess_SetWorkingDirectory(cp, dir);
180 181
  }
  if (cmSystemTools::GetRunCommandHideConsole()) {
182
    cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
183
  }
184
  cmsysProcess_SetOption(cp, cmsysProcess_Option_Verbatim, 1);
185
  const char* cmd[] = { command.c_str(), nullptr };
186 187 188
  cmsysProcess_SetCommand(cp, cmd);
#else
  std::string commandInDir;
189
  if (dir) {
190 191 192 193
    commandInDir = "cd \"";
    commandInDir += dir;
    commandInDir += "\" && ";
    commandInDir += command;
194
  } else {
195
    commandInDir = command;
196
  }
197
#  ifndef __VMS
198
  commandInDir += " 2>&1";
199
#  endif
200
  command = commandInDir;
201
  if (verbose) {
202 203 204
    cmSystemTools::Stdout("running ");
    cmSystemTools::Stdout(command);
    cmSystemTools::Stdout("\n");
205
  }
206 207
  fflush(stdout);
  fflush(stderr);
208
  const char* cmd[] = { "/bin/sh", "-c", command.c_str(), nullptr };
209 210 211 212 213 214 215 216 217
  cmsysProcess_SetCommand(cp, cmd);
#endif

  cmsysProcess_Execute(cp);

  // Read the process output.
  int length;
  char* data;
  int p;
218
  cmProcessOutput processOutput(encoding);
219
  std::string strdata;
220
  while ((p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
221 222
    if (p == cmsysProcess_Pipe_STDOUT || p == cmsysProcess_Pipe_STDERR) {
      if (verbose) {
223
        processOutput.DecodeText(data, length, strdata);
224
        cmSystemTools::Stdout(strdata);
225
      }
226
      output.append(data, length);
227
    }
228
  }
229

230 231 232
  if (verbose) {
    processOutput.DecodeText(std::string(), strdata);
    if (!strdata.empty()) {
233
      cmSystemTools::Stdout(strdata);
234 235 236
    }
  }

237
  // All output has been read.  Wait for the process to exit.
Daniel Pfeifer's avatar
Daniel Pfeifer committed
238
  cmsysProcess_WaitForExit(cp, nullptr);
239
  processOutput.DecodeText(output, output);
240 241 242

  // Check the result of running the process.
  std::string msg;
243
  switch (cmsysProcess_GetState(cp)) {
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
    case cmsysProcess_State_Exited:
      retVal = cmsysProcess_GetExitValue(cp);
      break;
    case cmsysProcess_State_Exception:
      retVal = -1;
      msg += "\nProcess terminated due to: ";
      msg += cmsysProcess_GetExceptionString(cp);
      break;
    case cmsysProcess_State_Error:
      retVal = -1;
      msg += "\nProcess failed because: ";
      msg += cmsysProcess_GetErrorString(cp);
      break;
    case cmsysProcess_State_Expired:
      retVal = -1;
      msg += "\nProcess terminated due to timeout.";
      break;
261 262
  }
  if (!msg.empty()) {
263
#if defined(_WIN32) && !defined(__CYGWIN__)
264 265 266
    // Old Windows process execution printed this info.
    msg += "\n\nfor command: ";
    msg += command;
267
    if (dir) {
268 269
      msg += "\nin dir: ";
      msg += dir;
270
    }
271
    msg += "\n";
272
    if (verbose) {
273
      cmSystemTools::Stdout(msg);
274
    }
275 276 277 278 279
    output += msg;
#else
    // Old UNIX process execution only put message in output.
    output += msg;
#endif
280
  }
281 282 283 284 285 286

  // Delete the process instance.
  cmsysProcess_Delete(cp);

  return true;
}