Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmExecuteProcessCommand.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
#include "cmExecuteProcessCommand.h"
4
5
#include <cstdint>
6
#include <cstdio>
7
#include <iostream>
8
#include <map>
9
#include <memory>
10
#include <sstream>
11
#include <utility>
12
#include <vector>
13
14
#include <cm/optional>
15
#include <cm/string_view>
16
#include <cmext/algorithm>
17
#include <cmext/string_view>
18
19
#include <cm3p/uv.h>
20
21
#ifndef _WIN32
22
#  include <fcntl.h>
23
24
#  include "cm_fileno.hxx"
25
#endif
26
27
#include "cmsys/String.h"
28
29
#include "cmArgumentParser.h"
30
#include "cmExecutionStatus.h"
31
#include "cmList.h"
32
#include "cmMakefile.h"
33
#include "cmMessageType.h"
34
#include "cmPolicies.h"
35
#include "cmProcessOutput.h"
36
#include "cmStringAlgorithms.h"
37
#include "cmSystemTools.h"
38
#include "cmUVHandlePtr.h"
39
#include "cmUVProcessChain.h"
40
#include "cmUVStream.h"
41
42
namespace {
43
bool cmExecuteProcessCommandIsWhitespace(char c)
44
0
{
45
0
  return (cmsysString_isspace(c) || c == '\n' || c == '\r');
46
0
}
47
48
FILE* FopenCLOEXEC(std::string const& path, char const* mode)
49
0
{
50
0
  FILE* f = cmsys::SystemTools::Fopen(path, mode);
51
0
#ifndef _WIN32
52
0
  if (f) {
53
0
    if (fcntl(cm_fileno(f), F_SETFD, FD_CLOEXEC) < 0) {
54
0
      fclose(f);
55
0
      f = nullptr;
56
0
    }
57
0
  }
58
0
#endif
59
0
  return f;
60
0
}
61
62
void cmExecuteProcessCommandFixText(std::vector<char>& output,
63
                                    bool strip_trailing_whitespace);
64
void cmExecuteProcessCommandAppend(std::vector<char>& output, char const* data,
65
                                   std::size_t length);
66
}
67
68
// cmExecuteProcessCommand
69
bool cmExecuteProcessCommand(std::vector<std::string> const& args,
70
                             cmExecutionStatus& status)
71
0
{
72
0
  if (args.empty()) {
73
0
    status.SetError("called with incorrect number of arguments");
74
0
    return false;
75
0
  }
76
77
0
  struct Arguments : public ArgumentParser::ParseResult
78
0
  {
79
0
    std::vector<std::vector<std::string>> Commands;
80
0
    std::string OutputVariable;
81
0
    std::string ErrorVariable;
82
0
    std::string ResultVariable;
83
0
    std::string ResultsVariable;
84
0
    std::string WorkingDirectory;
85
0
    std::string InputFile;
86
0
    std::string OutputFile;
87
0
    std::string ErrorFile;
88
0
    std::string Timeout;
89
0
    std::string CommandEcho;
90
0
    bool OutputQuiet = false;
91
0
    bool ErrorQuiet = false;
92
0
    bool OutputStripTrailingWhitespace = false;
93
0
    bool ErrorStripTrailingWhitespace = false;
94
0
    bool EchoOutputVariable = false;
95
0
    bool EchoErrorVariable = false;
96
0
    cm::optional<std::string> Encoding;
97
0
    std::string CommandErrorIsFatal;
98
0
  };
99
100
0
  static auto const parser =
101
0
    cmArgumentParser<Arguments>{}
102
0
      .Bind("COMMAND"_s, &Arguments::Commands)
103
0
      .Bind("COMMAND_ECHO"_s, &Arguments::CommandEcho)
104
0
      .Bind("OUTPUT_VARIABLE"_s, &Arguments::OutputVariable)
105
0
      .Bind("ERROR_VARIABLE"_s, &Arguments::ErrorVariable)
106
0
      .Bind("RESULT_VARIABLE"_s, &Arguments::ResultVariable)
107
0
      .Bind("RESULTS_VARIABLE"_s, &Arguments::ResultsVariable)
108
0
      .Bind("WORKING_DIRECTORY"_s, &Arguments::WorkingDirectory)
109
0
      .Bind("INPUT_FILE"_s, &Arguments::InputFile)
110
0
      .Bind("OUTPUT_FILE"_s, &Arguments::OutputFile)
111
0
      .Bind("ERROR_FILE"_s, &Arguments::ErrorFile)
112
0
      .Bind("TIMEOUT"_s, &Arguments::Timeout)
113
0
      .Bind("OUTPUT_QUIET"_s, &Arguments::OutputQuiet)
114
0
      .Bind("ERROR_QUIET"_s, &Arguments::ErrorQuiet)
115
0
      .Bind("OUTPUT_STRIP_TRAILING_WHITESPACE"_s,
116
0
            &Arguments::OutputStripTrailingWhitespace)
117
0
      .Bind("ERROR_STRIP_TRAILING_WHITESPACE"_s,
118
0
            &Arguments::ErrorStripTrailingWhitespace)
119
0
      .Bind("ENCODING"_s, &Arguments::Encoding)
120
0
      .Bind("ECHO_OUTPUT_VARIABLE"_s, &Arguments::EchoOutputVariable)
121
0
      .Bind("ECHO_ERROR_VARIABLE"_s, &Arguments::EchoErrorVariable)
122
0
      .Bind("COMMAND_ERROR_IS_FATAL"_s, &Arguments::CommandErrorIsFatal);
123
124
0
  std::vector<std::string> unparsedArguments;
125
0
  Arguments arguments = parser.Parse(args, &unparsedArguments);
126
127
0
  if (arguments.MaybeReportError(status.GetMakefile())) {
128
0
    return true;
129
0
  }
130
0
  if (!unparsedArguments.empty()) {
131
0
    status.SetError(" given unknown argument \"" + unparsedArguments.front() +
132
0
                    "\".");
133
0
    return false;
134
0
  }
135
136
0
  std::string inputFilename = arguments.InputFile;
137
0
  std::string outputFilename = arguments.OutputFile;
138
0
  std::string errorFilename = arguments.ErrorFile;
139
0
  if (!arguments.WorkingDirectory.empty()) {
140
0
    if (!inputFilename.empty()) {
141
0
      inputFilename = cmSystemTools::CollapseFullPath(
142
0
        inputFilename, arguments.WorkingDirectory);
143
0
    }
144
0
    if (!outputFilename.empty()) {
145
0
      outputFilename = cmSystemTools::CollapseFullPath(
146
0
        outputFilename, arguments.WorkingDirectory);
147
0
    }
148
0
    if (!errorFilename.empty()) {
149
0
      errorFilename = cmSystemTools::CollapseFullPath(
150
0
        errorFilename, arguments.WorkingDirectory);
151
0
    }
152
0
  }
153
154
0
  if (!status.GetMakefile().CanIWriteThisFile(outputFilename)) {
155
0
    status.SetError("attempted to output into a file: " + outputFilename +
156
0
                    " into a source directory.");
157
0
    cmSystemTools::SetFatalErrorOccurred();
158
0
    return false;
159
0
  }
160
161
  // Check for commands given.
162
0
  if (arguments.Commands.empty()) {
163
0
    status.SetError(" called with no COMMAND argument.");
164
0
    return false;
165
0
  }
166
0
  for (std::vector<std::string>& cmd : arguments.Commands) {
167
0
    if (cmd.empty()) {
168
0
      status.SetError(" given COMMAND argument with no value.");
169
0
      return false;
170
0
    }
171
0
    cmSystemTools::MaybePrependCmdExe(cmd);
172
0
  }
173
174
  // Parse the timeout string.
175
0
  double timeout = -1;
176
0
  if (!arguments.Timeout.empty()) {
177
0
    if (sscanf(arguments.Timeout.c_str(), "%lg", &timeout) != 1) {
178
0
      status.SetError(" called with TIMEOUT value that could not be parsed.");
179
0
      return false;
180
0
    }
181
0
  }
182
183
0
  std::string commandErrorIsFatal = arguments.CommandErrorIsFatal;
184
0
  if (commandErrorIsFatal.empty() && arguments.ResultVariable.empty() &&
185
0
      arguments.ResultsVariable.empty()) {
186
0
    commandErrorIsFatal = status.GetMakefile().GetSafeDefinition(
187
0
      "CMAKE_EXECUTE_PROCESS_COMMAND_ERROR_IS_FATAL");
188
0
  }
189
190
0
  if (!commandErrorIsFatal.empty() && commandErrorIsFatal != "ANY"_s &&
191
0
      commandErrorIsFatal != "LAST"_s && commandErrorIsFatal != "NONE"_s) {
192
0
    if (!arguments.CommandErrorIsFatal.empty()) {
193
0
      status.SetError(
194
0
        "COMMAND_ERROR_IS_FATAL option can be ANY, LAST or NONE");
195
0
      return false;
196
0
    }
197
0
    status.SetError(cmStrCat(
198
0
      "Using CMAKE_EXECUTE_PROCESS_COMMAND_ERROR_IS_FATAL with invalid value "
199
0
      "\"",
200
0
      commandErrorIsFatal, "\". This variable can be ANY, LAST or NONE"));
201
0
    return false;
202
0
  }
203
  // Create a process instance.
204
0
  cmUVProcessChainBuilder builder;
205
206
  // Set the command sequence.
207
0
  for (std::vector<std::string> const& cmd : arguments.Commands) {
208
0
    builder.AddCommand(cmd);
209
0
  }
210
211
  // Set the process working directory.
212
0
  if (!arguments.WorkingDirectory.empty()) {
213
0
    builder.SetWorkingDirectory(arguments.WorkingDirectory);
214
0
  }
215
216
  // Check the output variables.
217
0
  std::unique_ptr<FILE, int (*)(FILE*)> inputFile(nullptr, fclose);
218
0
  if (!inputFilename.empty()) {
219
0
    inputFile.reset(FopenCLOEXEC(inputFilename, "rb"));
220
0
    if (inputFile) {
221
0
      builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT,
222
0
                                inputFile.get());
223
0
    }
224
0
  } else {
225
0
    builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, stdin);
226
0
  }
227
228
0
  std::unique_ptr<FILE, int (*)(FILE*)> outputFile(nullptr, fclose);
229
0
  if (!outputFilename.empty()) {
230
0
    outputFile.reset(FopenCLOEXEC(outputFilename, "wb"));
231
0
    if (outputFile) {
232
0
      builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
233
0
                                outputFile.get());
234
0
    }
235
0
  } else {
236
0
    if (arguments.OutputVariable == arguments.ErrorVariable &&
237
0
        !arguments.ErrorVariable.empty()) {
238
0
      builder.SetMergedBuiltinStreams();
239
0
    } else {
240
0
      builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
241
0
    }
242
0
  }
243
244
0
  std::unique_ptr<FILE, int (*)(FILE*)> errorFile(nullptr, fclose);
245
0
  if (!errorFilename.empty()) {
246
0
    if (errorFilename == outputFilename) {
247
0
      if (outputFile) {
248
0
        builder.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
249
0
                                  outputFile.get());
250
0
      }
251
0
    } else {
252
0
      errorFile.reset(FopenCLOEXEC(errorFilename, "wb"));
253
0
      if (errorFile) {
254
0
        builder.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
255
0
                                  errorFile.get());
256
0
      }
257
0
    }
258
0
  } else if (arguments.ErrorVariable.empty() ||
259
0
             (!arguments.ErrorVariable.empty() &&
260
0
              arguments.OutputVariable != arguments.ErrorVariable)) {
261
0
    builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
262
0
  }
263
264
  // Set the timeout if any.
265
0
  int64_t timeoutMillis = static_cast<int64_t>(timeout * 1000.0);
266
267
0
  bool echo_stdout = false;
268
0
  bool echo_stderr = false;
269
0
  bool echo_output_from_variable = true;
270
0
  std::string echo_output = status.GetMakefile().GetSafeDefinition(
271
0
    "CMAKE_EXECUTE_PROCESS_COMMAND_ECHO");
272
0
  if (!arguments.CommandEcho.empty()) {
273
0
    echo_output_from_variable = false;
274
0
    echo_output = arguments.CommandEcho;
275
0
  }
276
277
0
  if (!echo_output.empty()) {
278
0
    if (echo_output == "STDERR") {
279
0
      echo_stderr = true;
280
0
    } else if (echo_output == "STDOUT") {
281
0
      echo_stdout = true;
282
0
    } else if (echo_output != "NONE") {
283
0
      std::string error;
284
0
      if (echo_output_from_variable) {
285
0
        error = "CMAKE_EXECUTE_PROCESS_COMMAND_ECHO set to '";
286
0
      } else {
287
0
        error = " called with '";
288
0
      }
289
0
      error += echo_output;
290
0
      error += "' expected STDERR|STDOUT|NONE";
291
0
      if (!echo_output_from_variable) {
292
0
        error += " for COMMAND_ECHO.";
293
0
      }
294
0
      status.GetMakefile().IssueMessage(MessageType::FATAL_ERROR, error);
295
0
      return true;
296
0
    }
297
0
  }
298
0
  if (echo_stdout || echo_stderr) {
299
0
    std::string command;
300
0
    for (auto const& cmd : arguments.Commands) {
301
0
      command += "'";
302
0
      command += cmJoin(cmd, "' '");
303
0
      command += "'";
304
0
      command += "\n";
305
0
    }
306
0
    if (echo_stdout) {
307
0
      std::cout << command;
308
0
    } else if (echo_stderr) {
309
0
      std::cerr << command;
310
0
    }
311
0
  }
312
  // Start the process.
313
0
  auto chain = builder.Start();
314
315
0
  bool timedOut = false;
316
0
  cm::uv_timer_ptr timer;
317
318
0
  if (timeoutMillis >= 0) {
319
0
    timer.init(chain.GetLoop(), &timedOut);
320
0
    timer.start(
321
0
      [](uv_timer_t* handle) {
322
0
        auto* timeoutPtr = static_cast<bool*>(handle->data);
323
0
        *timeoutPtr = true;
324
0
      },
325
0
      timeoutMillis, 0, cm::uv_update_time::yes);
326
0
  }
327
328
  // Read the process output.
329
0
  struct ReadData
330
0
  {
331
0
    uv_stream_t* Stream = nullptr;
332
0
    bool Finished = false;
333
0
    std::vector<char> Output;
334
0
  };
335
0
  ReadData outputData;
336
0
  ReadData errorData;
337
0
  outputData.Stream = chain.OutputStream();
338
0
  errorData.Stream = chain.ErrorStream();
339
0
  cmPolicies::PolicyStatus const cmp0176 =
340
0
    status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0176);
341
0
  cmProcessOutput::Encoding encoding =
342
0
    cmp0176 == cmPolicies::OLD || cmp0176 == cmPolicies::WARN
343
0
    ? cmProcessOutput::Auto
344
0
    : cmProcessOutput::UTF8;
345
0
  if (arguments.Encoding) {
346
0
    if (cm::optional<cmProcessOutput::Encoding> maybeEncoding =
347
0
          cmProcessOutput::FindEncoding(*arguments.Encoding)) {
348
0
      encoding = *maybeEncoding;
349
0
    } else {
350
0
      status.GetMakefile().IssueMessage(
351
0
        MessageType::AUTHOR_WARNING,
352
0
        cmStrCat("ENCODING option given unknown value \"", *arguments.Encoding,
353
0
                 "\".  Ignoring."));
354
0
    }
355
0
  }
356
0
  cmProcessOutput processOutput(encoding);
357
0
  std::string strdata;
358
359
0
  std::unique_ptr<cmUVStreamReadHandle> outputHandle;
360
0
  if (outputData.Stream) {
361
0
    outputHandle = cmUVStreamRead(
362
0
      outputData.Stream,
363
0
      [&arguments, &processOutput, &outputData,
364
0
       &strdata](std::vector<char> data) {
365
0
        if (!arguments.OutputQuiet) {
366
0
          if (arguments.OutputVariable.empty() ||
367
0
              arguments.EchoOutputVariable) {
368
0
            processOutput.DecodeText(data.data(), data.size(), strdata, 1);
369
0
            cmSystemTools::Stdout(strdata);
370
0
          }
371
0
          if (!arguments.OutputVariable.empty()) {
372
0
            cmExecuteProcessCommandAppend(outputData.Output, data.data(),
373
0
                                          data.size());
374
0
          }
375
0
        }
376
0
      },
377
0
      [&outputData]() { outputData.Finished = true; });
378
0
  } else {
379
0
    outputData.Finished = true;
380
0
  }
381
0
  std::unique_ptr<cmUVStreamReadHandle> errorHandle;
382
0
  if (errorData.Stream) {
383
0
    errorHandle = cmUVStreamRead(
384
0
      errorData.Stream,
385
0
      [&arguments, &processOutput, &errorData,
386
0
       &strdata](std::vector<char> data) {
387
0
        if (!arguments.ErrorQuiet) {
388
0
          if (arguments.ErrorVariable.empty() || arguments.EchoErrorVariable) {
389
0
            processOutput.DecodeText(data.data(), data.size(), strdata, 2);
390
0
            cmSystemTools::Stderr(strdata);
391
0
          }
392
0
          if (!arguments.ErrorVariable.empty()) {
393
0
            cmExecuteProcessCommandAppend(errorData.Output, data.data(),
394
0
                                          data.size());
395
0
          }
396
0
        }
397
0
      },
398
0
      [&errorData]() { errorData.Finished = true; });
399
0
  } else {
400
0
    errorData.Finished = true;
401
0
  }
402
403
0
  while (chain.Valid() && !timedOut &&
404
0
         !(chain.Finished() && outputData.Finished && errorData.Finished)) {
405
0
    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
406
0
  }
407
0
  if (timedOut) {
408
0
    chain.Terminate();
409
0
  }
410
0
  if (!arguments.OutputQuiet &&
411
0
      (arguments.OutputVariable.empty() || arguments.EchoOutputVariable)) {
412
0
    processOutput.DecodeText(std::string(), strdata, 1);
413
0
    if (!strdata.empty()) {
414
0
      cmSystemTools::Stdout(strdata);
415
0
    }
416
0
  }
417
0
  if (!arguments.ErrorQuiet &&
418
0
      (arguments.ErrorVariable.empty() || arguments.EchoErrorVariable)) {
419
0
    processOutput.DecodeText(std::string(), strdata, 2);
420
0
    if (!strdata.empty()) {
421
0
      cmSystemTools::Stderr(strdata);
422
0
    }
423
0
  }
424
425
  // All output has been read.
426
0
  processOutput.DecodeText(outputData.Output, outputData.Output);
427
0
  processOutput.DecodeText(errorData.Output, errorData.Output);
428
429
  // Fix the text in the output strings.
430
0
  cmExecuteProcessCommandFixText(outputData.Output,
431
0
                                 arguments.OutputStripTrailingWhitespace);
432
0
  cmExecuteProcessCommandFixText(errorData.Output,
433
0
                                 arguments.ErrorStripTrailingWhitespace);
434
435
  // Store the output obtained.
436
0
  if (!arguments.OutputVariable.empty() && !outputData.Output.empty()) {
437
0
    status.GetMakefile().AddDefinition(arguments.OutputVariable,
438
0
                                       outputData.Output.data());
439
0
  }
440
0
  if (arguments.ErrorVariable != arguments.OutputVariable &&
441
0
      !arguments.ErrorVariable.empty() && !errorData.Output.empty()) {
442
0
    status.GetMakefile().AddDefinition(arguments.ErrorVariable,
443
0
                                       errorData.Output.data());
444
0
  }
445
446
  // Store the result of running the process.
447
0
  if (!arguments.ResultVariable.empty()) {
448
0
    if (timedOut) {
449
0
      status.GetMakefile().AddDefinition(arguments.ResultVariable,
450
0
                                         "Process terminated due to timeout");
451
0
    } else {
452
0
      auto const* lastStatus = chain.GetStatus().back();
453
0
      auto exception = lastStatus->GetException();
454
0
      if (exception.first == cmUVProcessChain::ExceptionCode::None) {
455
0
        status.GetMakefile().AddDefinition(
456
0
          arguments.ResultVariable,
457
0
          std::to_string(static_cast<int>(lastStatus->ExitStatus)));
458
0
      } else {
459
0
        status.GetMakefile().AddDefinition(arguments.ResultVariable,
460
0
                                           exception.second);
461
0
      }
462
0
    }
463
0
  }
464
  // Store the result of running the processes.
465
0
  if (!arguments.ResultsVariable.empty()) {
466
0
    if (timedOut) {
467
0
      status.GetMakefile().AddDefinition(arguments.ResultsVariable,
468
0
                                         "Process terminated due to timeout");
469
0
    } else {
470
0
      std::vector<std::string> res;
471
0
      for (auto const* processStatus : chain.GetStatus()) {
472
0
        auto exception = processStatus->GetException();
473
0
        if (exception.first == cmUVProcessChain::ExceptionCode::None) {
474
0
          res.emplace_back(
475
0
            std::to_string(static_cast<int>(processStatus->ExitStatus)));
476
0
        } else {
477
0
          res.emplace_back(exception.second);
478
0
        }
479
0
      }
480
0
      status.GetMakefile().AddDefinition(arguments.ResultsVariable,
481
0
                                         cmList::to_string(res));
482
0
    }
483
0
  }
484
485
0
  auto queryProcessStatusByIndex = [&chain](std::size_t index) -> std::string {
486
0
    auto const& processStatus = chain.GetStatus(index);
487
0
    auto exception = processStatus.GetException();
488
0
    if (exception.first == cmUVProcessChain::ExceptionCode::None) {
489
0
      if (processStatus.ExitStatus) {
490
0
        return cmStrCat("Child return code: ", processStatus.ExitStatus);
491
0
      }
492
0
      return "";
493
0
    }
494
0
    return cmStrCat("Abnormal exit with child return code: ",
495
0
                    exception.second);
496
0
  };
497
498
0
  if (commandErrorIsFatal == "ANY"_s) {
499
0
    bool ret = true;
500
0
    if (timedOut) {
501
0
      status.SetError("Process terminated due to timeout");
502
0
      ret = false;
503
0
    } else {
504
0
      std::map<std::size_t, std::string> failureIndices;
505
0
      auto statuses = chain.GetStatus();
506
0
      for (std::size_t i = 0; i < statuses.size(); ++i) {
507
0
        std::string processStatus = queryProcessStatusByIndex(i);
508
0
        if (!processStatus.empty()) {
509
0
          failureIndices[i] = processStatus;
510
0
        }
511
0
      }
512
0
      if (!failureIndices.empty()) {
513
0
        std::ostringstream oss;
514
0
        oss << "failed command indexes:\n";
515
0
        for (auto const& e : failureIndices) {
516
0
          oss << "  " << e.first + 1 << ": \"" << e.second << "\"\n";
517
0
        }
518
0
        status.SetError(oss.str());
519
0
        ret = false;
520
0
      }
521
0
    }
522
523
0
    if (!ret) {
524
0
      cmSystemTools::SetFatalErrorOccurred();
525
0
      return false;
526
0
    }
527
0
  }
528
529
0
  if (commandErrorIsFatal == "LAST"_s) {
530
0
    bool ret = true;
531
0
    if (timedOut) {
532
0
      status.SetError("Process terminated due to timeout");
533
0
      ret = false;
534
0
    } else {
535
0
      auto const& lastStatus = chain.GetStatus(arguments.Commands.size() - 1);
536
0
      auto exception = lastStatus.GetException();
537
0
      if (exception.first != cmUVProcessChain::ExceptionCode::None) {
538
0
        status.SetError(cmStrCat("Abnormal exit: ", exception.second));
539
0
        ret = false;
540
0
      } else {
541
0
        int lastIndex = static_cast<int>(arguments.Commands.size() - 1);
542
0
        std::string const processStatus = queryProcessStatusByIndex(lastIndex);
543
0
        if (!processStatus.empty()) {
544
0
          status.SetError("last command failed");
545
0
          ret = false;
546
0
        }
547
0
      }
548
0
    }
549
0
    if (!ret) {
550
0
      cmSystemTools::SetFatalErrorOccurred();
551
0
      return false;
552
0
    }
553
0
  }
554
555
0
  return true;
556
0
}
557
558
namespace {
559
void cmExecuteProcessCommandFixText(std::vector<char>& output,
560
                                    bool strip_trailing_whitespace)
561
0
{
562
  // Remove \0 characters and the \r part of \r\n pairs.
563
0
  unsigned int in_index = 0;
564
0
  unsigned int out_index = 0;
565
0
  while (in_index < output.size()) {
566
0
    char c = output[in_index++];
567
0
    if ((c != '\r' ||
568
0
         !(in_index < output.size() && output[in_index] == '\n')) &&
569
0
        c != '\0') {
570
0
      output[out_index++] = c;
571
0
    }
572
0
  }
573
574
  // Remove trailing whitespace if requested.
575
0
  if (strip_trailing_whitespace) {
576
0
    while (out_index > 0 &&
577
0
           cmExecuteProcessCommandIsWhitespace(output[out_index - 1])) {
578
0
      --out_index;
579
0
    }
580
0
  }
581
582
  // Shrink the vector to the size needed.
583
0
  output.resize(out_index);
584
585
  // Put a terminator on the text string.
586
0
  output.push_back('\0');
587
0
}
588
589
void cmExecuteProcessCommandAppend(std::vector<char>& output, char const* data,
590
                                   std::size_t length)
591
0
{
592
#if defined(__APPLE__)
593
  // HACK on Apple to work around bug with inserting at the
594
  // end of an empty vector.  This resulted in random failures
595
  // that were hard to reproduce.
596
  if (output.empty() && length > 0) {
597
    output.push_back(data[0]);
598
    ++data;
599
    --length;
600
  }
601
#endif
602
0
  cm::append(output, data, data + length);
603
0
}
604
}