resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmExecProgramCommand.cxx
1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include "cmExecProgramCommand.h"
4
5 #include <cstdio>
6
7 #include "cmsys/Process.h"
8
9 #include "cmExecutionStatus.h"
10 #include "cmMakefile.h"
11 #include "cmProcessOutput.h"
12 #include "cmStringAlgorithms.h"
13 #include "cmSystemTools.h"
14
15 using Encoding = cmProcessOutput::Encoding;
16
17 namespace {
18 bool RunCommand(std::string command, std::string& output, int& retVal,
19                 const char* directory = nullptr, bool verbose = true,
20                 Encoding encoding = cmProcessOutput::Auto);
21 }
22
23 // cmExecProgramCommand
24 bool cmExecProgramCommand(std::vector<std::string> const& args,
25                           cmExecutionStatus& status)
26 {
27   if (args.empty()) {
28     status.SetError("called with incorrect number of arguments");
29     return false;
30   }
31   std::string arguments;
32   bool doingargs = false;
33   int count = 0;
34   std::string output_variable;
35   bool haveoutput_variable = false;
36   std::string return_variable;
37   bool havereturn_variable = false;
38   for (std::string const& arg : args) {
39     if (arg == "OUTPUT_VARIABLE") {
40       count++;
41       doingargs = false;
42       havereturn_variable = false;
43       haveoutput_variable = true;
44     } else if (haveoutput_variable) {
45       if (!output_variable.empty()) {
46         status.SetError("called with incorrect number of arguments");
47         return false;
48       }
49       output_variable = arg;
50       haveoutput_variable = false;
51       count++;
52     } else if (arg == "RETURN_VALUE") {
53       count++;
54       doingargs = false;
55       haveoutput_variable = false;
56       havereturn_variable = true;
57     } else if (havereturn_variable) {
58       if (!return_variable.empty()) {
59         status.SetError("called with incorrect number of arguments");
60         return false;
61       }
62       return_variable = arg;
63       havereturn_variable = false;
64       count++;
65     } else if (arg == "ARGS") {
66       count++;
67       havereturn_variable = false;
68       haveoutput_variable = false;
69       doingargs = true;
70     } else if (doingargs) {
71       arguments += arg;
72       arguments += " ";
73       count++;
74     }
75   }
76
77   std::string command;
78   if (!arguments.empty()) {
79     command = cmStrCat(cmSystemTools::ConvertToRunCommandPath(args[0]), ' ',
80                        arguments);
81   } else {
82     command = args[0];
83   }
84   bool verbose = true;
85   if (!output_variable.empty()) {
86     verbose = false;
87   }
88   int retVal = 0;
89   std::string output;
90   bool result = true;
91   if (args.size() - count == 2) {
92     cmSystemTools::MakeDirectory(args[1]);
93     result = RunCommand(command, output, retVal, args[1].c_str(), verbose);
94   } else {
95     result = RunCommand(command, output, retVal, nullptr, verbose);
96   }
97   if (!result) {
98     retVal = -1;
99   }
100
101   if (!output_variable.empty()) {
102     std::string::size_type first = output.find_first_not_of(" \n\t\r");
103     std::string::size_type last = output.find_last_not_of(" \n\t\r");
104     if (first == std::string::npos) {
105       first = 0;
106     }
107     if (last == std::string::npos) {
108       last = output.size() - 1;
109     }
110
111     std::string coutput = std::string(output, first, last - first + 1);
112     status.GetMakefile().AddDefinition(output_variable, coutput);
113   }
114
115   if (!return_variable.empty()) {
116     char buffer[100];
117     snprintf(buffer, sizeof(buffer), "%d", retVal);
118     status.GetMakefile().AddDefinition(return_variable, buffer);
119   }
120
121   return true;
122 }
123
124 namespace {
125 bool RunCommand(std::string command, std::string& output, int& retVal,
126                 const char* dir, bool verbose, Encoding encoding)
127 {
128   if (cmSystemTools::GetRunCommandOutput()) {
129     verbose = false;
130   }
131
132 #if defined(_WIN32) && !defined(__CYGWIN__)
133   // if the command does not start with a quote, then
134   // try to find the program, and if the program can not be
135   // found use system to run the command as it must be a built in
136   // shell command like echo or dir
137   if (!command.empty() && command[0] == '\"') {
138     // count the number of quotes
139     int count = 0;
140     for (char c : command) {
141       if (c == '\"') {
142         count++;
143         if (count > 2) {
144           break;
145         }
146       }
147     }
148     // if there are more than two double quotes use
149     // GetShortPathName, the cmd.exe program in windows which
150     // is used by system fails to execute if there are more than
151     // one set of quotes in the arguments
152     if (count > 2) {
153       cmsys::RegularExpression quoted("^\"([^\"]*)\"[ \t](.*)");
154       if (quoted.find(command)) {
155         std::string shortCmd;
156         std::string cmd = quoted.match(1);
157         std::string args = quoted.match(2);
158         if (!cmSystemTools::FileExists(cmd)) {
159           shortCmd = cmd;
160         } else if (!cmSystemTools::GetShortPath(cmd, shortCmd)) {
161           cmSystemTools::Error("GetShortPath failed for " + cmd);
162           return false;
163         }
164         shortCmd += " ";
165         shortCmd += args;
166
167         command = shortCmd;
168       } else {
169         cmSystemTools::Error("Could not parse command line with quotes " +
170                              command);
171       }
172     }
173   }
174 #endif
175
176   // Allocate a process instance.
177   cmsysProcess* cp = cmsysProcess_New();
178   if (!cp) {
179     cmSystemTools::Error("Error allocating process instance.");
180     return false;
181   }
182
183 #if defined(_WIN32) && !defined(__CYGWIN__)
184   if (dir) {
185     cmsysProcess_SetWorkingDirectory(cp, dir);
186   }
187   if (cmSystemTools::GetRunCommandHideConsole()) {
188     cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
189   }
190   cmsysProcess_SetOption(cp, cmsysProcess_Option_Verbatim, 1);
191   const char* cmd[] = { command.c_str(), nullptr };
192   cmsysProcess_SetCommand(cp, cmd);
193 #else
194   std::string commandInDir;
195   if (dir) {
196     commandInDir = cmStrCat("cd \"", dir, "\" && ", command);
197   } else {
198     commandInDir = command;
199   }
200 #  ifndef __VMS
201   commandInDir += " 2>&1";
202 #  endif
203   command = commandInDir;
204   if (verbose) {
205     cmSystemTools::Stdout("running ");
206     cmSystemTools::Stdout(command);
207     cmSystemTools::Stdout("\n");
208   }
209   fflush(stdout);
210   fflush(stderr);
211   const char* cmd[] = { "/bin/sh", "-c", command.c_str(), nullptr };
212   cmsysProcess_SetCommand(cp, cmd);
213 #endif
214
215   cmsysProcess_Execute(cp);
216
217   // Read the process output.
218   int length;
219   char* data;
220   int p;
221   cmProcessOutput processOutput(encoding);
222   std::string strdata;
223   while ((p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
224     if (p == cmsysProcess_Pipe_STDOUT || p == cmsysProcess_Pipe_STDERR) {
225       if (verbose) {
226         processOutput.DecodeText(data, length, strdata);
227         cmSystemTools::Stdout(strdata);
228       }
229       output.append(data, length);
230     }
231   }
232
233   if (verbose) {
234     processOutput.DecodeText(std::string(), strdata);
235     if (!strdata.empty()) {
236       cmSystemTools::Stdout(strdata);
237     }
238   }
239
240   // All output has been read.  Wait for the process to exit.
241   cmsysProcess_WaitForExit(cp, nullptr);
242   processOutput.DecodeText(output, output);
243
244   // Check the result of running the process.
245   std::string msg;
246   switch (cmsysProcess_GetState(cp)) {
247     case cmsysProcess_State_Exited:
248       retVal = cmsysProcess_GetExitValue(cp);
249       break;
250     case cmsysProcess_State_Exception:
251       retVal = -1;
252       msg += "\nProcess terminated due to: ";
253       msg += cmsysProcess_GetExceptionString(cp);
254       break;
255     case cmsysProcess_State_Error:
256       retVal = -1;
257       msg += "\nProcess failed because: ";
258       msg += cmsysProcess_GetErrorString(cp);
259       break;
260     case cmsysProcess_State_Expired:
261       retVal = -1;
262       msg += "\nProcess terminated due to timeout.";
263       break;
264   }
265   if (!msg.empty()) {
266 #if defined(_WIN32) && !defined(__CYGWIN__)
267     // Old Windows process execution printed this info.
268     msg += "\n\nfor command: ";
269     msg += command;
270     if (dir) {
271       msg += "\nin dir: ";
272       msg += dir;
273     }
274     msg += "\n";
275     if (verbose) {
276       cmSystemTools::Stdout(msg);
277     }
278     output += msg;
279 #else
280     // Old UNIX process execution only put message in output.
281     output += msg;
282 #endif
283   }
284
285   // Delete the process instance.
286   cmsysProcess_Delete(cp);
287
288   return true;
289 }
290 }