Coverage Report

Created: 2026-02-09 06:05

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