Coverage Report

Created: 2026-04-29 07:01

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