Coverage Report

Created: 2026-04-29 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmFileCommand.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 "cmFileCommand.h"
4
5
#include <algorithm>
6
#include <cassert>
7
#include <cmath>
8
#include <cstdio>
9
#include <cstdlib>
10
#include <iterator>
11
#include <map>
12
#include <set>
13
#include <sstream>
14
#include <utility>
15
#include <vector>
16
17
#include <cm/memory>
18
#include <cm/optional>
19
#include <cm/string_view>
20
#include <cmext/algorithm>
21
#include <cmext/string_view>
22
23
#include <cm3p/kwiml/int.h>
24
25
#include "cmsys/FStream.hxx"
26
#include "cmsys/Glob.hxx"
27
#include "cmsys/RegularExpression.hxx"
28
#include "cmsys/String.h"
29
30
#include "cm_sys_stat.h"
31
32
#include "cmArgumentParser.h"
33
#include "cmArgumentParserTypes.h"
34
#include "cmCMakePath.h"
35
#include "cmCryptoHash.h"
36
#include "cmDiagnostics.h"
37
#include "cmELF.h"
38
#include "cmExecutionStatus.h"
39
#include "cmFSPermissions.h"
40
#include "cmFileCommand_ReadMacho.h"
41
#include "cmFileCopier.h"
42
#include "cmFileInstaller.h"
43
#include "cmFileLockPool.h"
44
#include "cmFileTimes.h"
45
#include "cmGeneratedFileStream.h"
46
#include "cmGeneratorExpression.h"
47
#include "cmGlobCacheEntry.h"
48
#include "cmGlobalGenerator.h"
49
#include "cmHexFileConverter.h"
50
#include "cmList.h"
51
#include "cmListFileCache.h"
52
#include "cmMakefile.h"
53
#include "cmMessageType.h"
54
#include "cmNewLineStyle.h"
55
#include "cmPolicies.h"
56
#include "cmRange.h"
57
#include "cmRuntimeDependencyArchive.h"
58
#include "cmState.h"
59
#include "cmStringAlgorithms.h"
60
#include "cmSubcommandTable.h"
61
#include "cmSystemTools.h"
62
#include "cmTimestamp.h"
63
#include "cmValue.h"
64
#include "cmWorkingDirectory.h"
65
#include "cmake.h"
66
67
#if !defined(CMAKE_BOOTSTRAP)
68
#  include <cm3p/curl/curl.h>
69
70
#  include "cmCurl.h"
71
#  include "cmFileLockResult.h"
72
#endif
73
74
namespace {
75
76
bool HandleWriteImpl(std::vector<std::string> const& args, bool append,
77
                     cmExecutionStatus& status)
78
0
{
79
0
  if (args.size() < 2) {
80
0
    status.SetError(cmStrCat(
81
0
      args[0], " must be called with at least one additional argument."));
82
0
    return false;
83
0
  }
84
0
  auto i = args.begin();
85
86
0
  i++; // Get rid of subcommand
87
88
0
  std::string fileName = *i;
89
0
  if (!cmsys::SystemTools::FileIsFullPath(*i)) {
90
0
    fileName =
91
0
      cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', *i);
92
0
  }
93
94
0
  i++;
95
96
0
  if (!status.GetMakefile().CanIWriteThisFile(fileName)) {
97
0
    std::string e = cmStrCat("attempted to write a file: ", fileName,
98
0
                             " into a source directory.");
99
0
    status.SetError(e);
100
0
    cmSystemTools::SetFatalErrorOccurred();
101
0
    return false;
102
0
  }
103
0
  std::string dir = cmSystemTools::GetFilenamePath(fileName);
104
0
  cmSystemTools::MakeDirectory(dir);
105
106
0
  mode_t mode = 0;
107
0
  bool writable = false;
108
109
  // Set permissions to writable
110
0
  if (cmSystemTools::GetPermissions(fileName, mode)) {
111
#if defined(_MSC_VER) || defined(__MINGW32__)
112
    writable = (mode & S_IWRITE) != 0;
113
    mode_t newMode = mode | S_IWRITE;
114
#else
115
0
    writable = mode & S_IWUSR;
116
0
    mode_t newMode = mode | S_IWUSR | S_IWGRP;
117
0
#endif
118
0
    if (!writable) {
119
0
      cmSystemTools::SetPermissions(fileName, newMode);
120
0
    }
121
0
  }
122
  // If GetPermissions fails, pretend like it is ok. File open will fail if
123
  // the file is not writable
124
0
  cmsys::ofstream file(fileName.c_str(),
125
0
                       append ? std::ios::app : std::ios::out);
126
0
  if (!file) {
127
0
    std::string error =
128
0
      cmStrCat("failed to open for writing (",
129
0
               cmSystemTools::GetLastSystemError(), "):\n  ", fileName);
130
0
    status.SetError(error);
131
0
    return false;
132
0
  }
133
0
  std::string message = cmJoin(cmMakeRange(i, args.end()), std::string());
134
0
  file << message;
135
0
  if (!file) {
136
0
    std::string error =
137
0
      cmStrCat("write failed (", cmSystemTools::GetLastSystemError(), "):\n  ",
138
0
               fileName);
139
0
    status.SetError(error);
140
0
    return false;
141
0
  }
142
0
  file.close();
143
0
  if (mode && !writable) {
144
0
    cmSystemTools::SetPermissions(fileName, mode);
145
0
  }
146
0
  return true;
147
0
}
148
149
bool HandleWriteCommand(std::vector<std::string> const& args,
150
                        cmExecutionStatus& status)
151
0
{
152
0
  return HandleWriteImpl(args, false, status);
153
0
}
154
155
bool HandleAppendCommand(std::vector<std::string> const& args,
156
                         cmExecutionStatus& status)
157
0
{
158
0
  return HandleWriteImpl(args, true, status);
159
0
}
160
161
bool HandleReadCommand(std::vector<std::string> const& args,
162
                       cmExecutionStatus& status)
163
0
{
164
0
  if (args.size() < 3) {
165
0
    status.SetError("READ must be called with at least two additional "
166
0
                    "arguments");
167
0
    return false;
168
0
  }
169
170
0
  std::string const& fileNameArg = args[1];
171
0
  std::string const& variable = args[2];
172
173
0
  struct Arguments
174
0
  {
175
0
    std::string Offset;
176
0
    std::string Limit;
177
0
    bool Hex = false;
178
0
  };
179
180
0
  static auto const parser = cmArgumentParser<Arguments>{}
181
0
                               .Bind("OFFSET"_s, &Arguments::Offset)
182
0
                               .Bind("LIMIT"_s, &Arguments::Limit)
183
0
                               .Bind("HEX"_s, &Arguments::Hex);
184
185
0
  Arguments const arguments = parser.Parse(cmMakeRange(args).advance(3),
186
0
                                           /*unparsedArguments=*/nullptr);
187
188
0
  std::string fileName = fileNameArg;
189
0
  if (!cmsys::SystemTools::FileIsFullPath(fileName)) {
190
0
    fileName = cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/',
191
0
                        fileNameArg);
192
0
  }
193
194
// Open the specified file.
195
#if defined(_WIN32) || defined(__CYGWIN__)
196
  cmsys::ifstream file(fileName.c_str(),
197
                       arguments.Hex ? (std::ios::binary | std::ios::in)
198
                                     : std::ios::in);
199
#else
200
0
  cmsys::ifstream file(fileName.c_str());
201
0
#endif
202
203
0
  if (!file) {
204
0
    std::string error =
205
0
      cmStrCat("failed to open for reading (",
206
0
               cmSystemTools::GetLastSystemError(), "):\n  ", fileName);
207
0
    status.SetError(error);
208
0
    return false;
209
0
  }
210
211
  // is there a limit?
212
0
  std::string::size_type sizeLimit = std::string::npos;
213
0
  if (!arguments.Limit.empty()) {
214
0
    unsigned long long limit;
215
0
    if (cmStrToULongLong(arguments.Limit, &limit)) {
216
0
      sizeLimit = static_cast<std::string::size_type>(limit);
217
0
    }
218
0
  }
219
220
  // is there an offset?
221
0
  cmsys::ifstream::off_type offset = 0;
222
0
  if (!arguments.Offset.empty()) {
223
0
    long long off;
224
0
    if (cmStrToLongLong(arguments.Offset, &off)) {
225
0
      offset = static_cast<cmsys::ifstream::off_type>(off);
226
0
    }
227
0
  }
228
229
0
  file.seekg(offset, std::ios::beg); // explicit ios::beg for IBM VisualAge 6
230
231
0
  std::string output;
232
233
0
  if (arguments.Hex) {
234
    // Convert part of the file into hex code
235
0
    char c;
236
0
    while ((sizeLimit > 0) && (file.get(c))) {
237
0
      char hex[4];
238
0
      snprintf(hex, sizeof(hex), "%.2x", c & 0xFFu);
239
0
      output += hex;
240
0
      sizeLimit--;
241
0
    }
242
0
  } else {
243
0
    std::string line;
244
0
    bool has_newline = false;
245
0
    while (
246
0
      sizeLimit > 0 &&
247
0
      cmSystemTools::GetLineFromStream(file, line, &has_newline, sizeLimit)) {
248
0
      sizeLimit = sizeLimit - line.size();
249
0
      if (has_newline && sizeLimit > 0) {
250
0
        sizeLimit--;
251
0
      }
252
0
      output += line;
253
0
      if (has_newline) {
254
0
        output += "\n";
255
0
      }
256
0
    }
257
0
  }
258
0
  status.GetMakefile().AddDefinition(variable, output);
259
0
  return true;
260
0
}
261
262
bool HandleHashCommand(std::vector<std::string> const& args,
263
                       cmExecutionStatus& status)
264
0
{
265
0
#if !defined(CMAKE_BOOTSTRAP)
266
0
  if (args.size() != 3) {
267
0
    status.SetError(
268
0
      cmStrCat(args[0], " requires a file name and output variable"));
269
0
    return false;
270
0
  }
271
272
0
  std::unique_ptr<cmCryptoHash> hash(cmCryptoHash::New(args[0]));
273
0
  if (hash) {
274
0
    std::string out = hash->HashFile(args[1]);
275
0
    if (!out.empty()) {
276
0
      status.GetMakefile().AddDefinition(args[2], out);
277
0
      return true;
278
0
    }
279
0
    status.SetError(cmStrCat(args[0], " failed to read file \"", args[1],
280
0
                             "\": ", cmSystemTools::GetLastSystemError()));
281
0
  }
282
0
  return false;
283
#else
284
  status.SetError(cmStrCat(args[0], " not available during bootstrap"));
285
  return false;
286
#endif
287
0
}
288
289
bool HandleStringsCommand(std::vector<std::string> const& args,
290
                          cmExecutionStatus& status)
291
0
{
292
0
  if (args.size() < 3) {
293
0
    status.SetError("STRINGS requires a file name and output variable");
294
0
    return false;
295
0
  }
296
297
  // Get the file to read.
298
0
  std::string fileName = args[1];
299
0
  if (!cmsys::SystemTools::FileIsFullPath(fileName)) {
300
0
    fileName =
301
0
      cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', args[1]);
302
0
  }
303
304
  // Get the variable in which to store the results.
305
0
  std::string const& outVar = args[2];
306
307
  // Parse the options.
308
0
  enum
309
0
  {
310
0
    arg_none,
311
0
    arg_limit_input,
312
0
    arg_limit_output,
313
0
    arg_limit_count,
314
0
    arg_length_minimum,
315
0
    arg_length_maximum,
316
0
    arg_maximum,
317
0
    arg_regex,
318
0
    arg_encoding
319
0
  };
320
0
  unsigned int minlen = 0;
321
0
  unsigned int maxlen = 0;
322
0
  int limit_input = -1;
323
0
  int limit_output = -1;
324
0
  unsigned int limit_count = 0;
325
0
  cmsys::RegularExpression regex;
326
0
  bool have_regex = false;
327
0
  bool store_regex = true;
328
0
  bool newline_consume = false;
329
0
  bool hex_conversion_enabled = true;
330
0
  enum
331
0
  {
332
0
    encoding_none = cmsys::FStream::BOM_None,
333
0
    encoding_utf8 = cmsys::FStream::BOM_UTF8,
334
0
    encoding_utf16le = cmsys::FStream::BOM_UTF16LE,
335
0
    encoding_utf16be = cmsys::FStream::BOM_UTF16BE,
336
0
    encoding_utf32le = cmsys::FStream::BOM_UTF32LE,
337
0
    encoding_utf32be = cmsys::FStream::BOM_UTF32BE
338
0
  };
339
0
  int encoding = encoding_none;
340
0
  int arg_mode = arg_none;
341
0
  for (unsigned int i = 3; i < args.size(); ++i) {
342
0
    if (args[i] == "LIMIT_INPUT") {
343
0
      arg_mode = arg_limit_input;
344
0
    } else if (args[i] == "LIMIT_OUTPUT") {
345
0
      arg_mode = arg_limit_output;
346
0
    } else if (args[i] == "LIMIT_COUNT") {
347
0
      arg_mode = arg_limit_count;
348
0
    } else if (args[i] == "LENGTH_MINIMUM") {
349
0
      arg_mode = arg_length_minimum;
350
0
    } else if (args[i] == "LENGTH_MAXIMUM") {
351
0
      arg_mode = arg_length_maximum;
352
0
    } else if (args[i] == "REGEX") {
353
0
      arg_mode = arg_regex;
354
0
    } else if (args[i] == "NEWLINE_CONSUME") {
355
0
      newline_consume = true;
356
0
      arg_mode = arg_none;
357
0
    } else if (args[i] == "NO_HEX_CONVERSION") {
358
0
      hex_conversion_enabled = false;
359
0
      arg_mode = arg_none;
360
0
    } else if (args[i] == "ENCODING") {
361
0
      arg_mode = arg_encoding;
362
0
    } else if (arg_mode == arg_limit_input) {
363
0
      if (sscanf(args[i].c_str(), "%d", &limit_input) != 1 ||
364
0
          limit_input < 0) {
365
0
        status.SetError(cmStrCat("STRINGS option LIMIT_INPUT value \"",
366
0
                                 args[i], "\" is not an unsigned integer."));
367
0
        return false;
368
0
      }
369
0
      arg_mode = arg_none;
370
0
    } else if (arg_mode == arg_limit_output) {
371
0
      if (sscanf(args[i].c_str(), "%d", &limit_output) != 1 ||
372
0
          limit_output < 0) {
373
0
        status.SetError(cmStrCat("STRINGS option LIMIT_OUTPUT value \"",
374
0
                                 args[i], "\" is not an unsigned integer."));
375
0
        return false;
376
0
      }
377
0
      arg_mode = arg_none;
378
0
    } else if (arg_mode == arg_limit_count) {
379
0
      int count;
380
0
      if (sscanf(args[i].c_str(), "%d", &count) != 1 || count < 0) {
381
0
        status.SetError(cmStrCat("STRINGS option LIMIT_COUNT value \"",
382
0
                                 args[i], "\" is not an unsigned integer."));
383
0
        return false;
384
0
      }
385
0
      limit_count = count;
386
0
      arg_mode = arg_none;
387
0
    } else if (arg_mode == arg_length_minimum) {
388
0
      int len;
389
0
      if (sscanf(args[i].c_str(), "%d", &len) != 1 || len < 0) {
390
0
        status.SetError(cmStrCat("STRINGS option LENGTH_MINIMUM value \"",
391
0
                                 args[i], "\" is not an unsigned integer."));
392
0
        return false;
393
0
      }
394
0
      minlen = len;
395
0
      arg_mode = arg_none;
396
0
    } else if (arg_mode == arg_length_maximum) {
397
0
      int len;
398
0
      if (sscanf(args[i].c_str(), "%d", &len) != 1 || len < 0) {
399
0
        status.SetError(cmStrCat("STRINGS option LENGTH_MAXIMUM value \"",
400
0
                                 args[i], "\" is not an unsigned integer."));
401
0
        return false;
402
0
      }
403
0
      maxlen = len;
404
0
      arg_mode = arg_none;
405
0
    } else if (arg_mode == arg_regex) {
406
0
      if (!regex.compile(args[i])) {
407
0
        status.SetError(cmStrCat("STRINGS option REGEX value \"", args[i],
408
0
                                 "\" could not be compiled."));
409
0
        return false;
410
0
      }
411
0
      have_regex = true;
412
0
      switch (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0159)) {
413
0
        case cmPolicies::NEW:
414
          // store_regex = true
415
0
          break;
416
0
        case cmPolicies::WARN:
417
0
          if (status.GetMakefile().PolicyOptionalWarningEnabled(
418
0
                "CMAKE_POLICY_WARNING_CMP0159")) {
419
0
            status.GetMakefile().IssueDiagnostic(
420
0
              cmDiagnostics::CMD_AUTHOR,
421
0
              cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0159),
422
0
                       "\n"
423
0
                       "For compatibility, CMake is leaving CMAKE_MATCH_<n> "
424
0
                       "unchanged."));
425
0
          }
426
0
          CM_FALLTHROUGH;
427
0
        case cmPolicies::OLD:
428
0
          store_regex = false;
429
0
          break;
430
0
      }
431
0
      arg_mode = arg_none;
432
0
    } else if (arg_mode == arg_encoding) {
433
0
      if (args[i] == "UTF-8") {
434
0
        encoding = encoding_utf8;
435
0
      } else if (args[i] == "UTF-16LE") {
436
0
        encoding = encoding_utf16le;
437
0
      } else if (args[i] == "UTF-16BE") {
438
0
        encoding = encoding_utf16be;
439
0
      } else if (args[i] == "UTF-32LE") {
440
0
        encoding = encoding_utf32le;
441
0
      } else if (args[i] == "UTF-32BE") {
442
0
        encoding = encoding_utf32be;
443
0
      } else {
444
0
        status.SetError(cmStrCat("STRINGS option ENCODING \"", args[i],
445
0
                                 "\" not recognized."));
446
0
        return false;
447
0
      }
448
0
      arg_mode = arg_none;
449
0
    } else {
450
0
      status.SetError(
451
0
        cmStrCat("STRINGS given unknown argument \"", args[i], '"'));
452
0
      return false;
453
0
    }
454
0
  }
455
456
0
  if (hex_conversion_enabled) {
457
    // TODO: should work without temp file, but just on a memory buffer
458
0
    std::string binaryFileName =
459
0
      cmStrCat(status.GetMakefile().GetCurrentBinaryDirectory(),
460
0
               "/CMakeFiles/FileCommandStringsBinaryFile");
461
0
    if (cmHexFileConverter::TryConvert(fileName, binaryFileName)) {
462
0
      fileName = binaryFileName;
463
0
    }
464
0
  }
465
466
// Open the specified file.
467
#if defined(_WIN32) || defined(__CYGWIN__)
468
  cmsys::ifstream fin(fileName.c_str(), std::ios::in | std::ios::binary);
469
#else
470
0
  cmsys::ifstream fin(fileName.c_str());
471
0
#endif
472
0
  if (!fin) {
473
0
    status.SetError(
474
0
      cmStrCat("STRINGS file \"", fileName, "\" cannot be read."));
475
0
    return false;
476
0
  }
477
478
  // If BOM is found and encoding was not specified, use the BOM
479
0
  int bom_found = cmsys::FStream::ReadBOM(fin);
480
0
  if (encoding == encoding_none && bom_found != cmsys::FStream::BOM_None) {
481
0
    encoding = bom_found;
482
0
  }
483
484
0
  unsigned int bytes_rem = 0;
485
0
  if (encoding == encoding_utf16le || encoding == encoding_utf16be) {
486
0
    bytes_rem = 1;
487
0
  }
488
0
  if (encoding == encoding_utf32le || encoding == encoding_utf32be) {
489
0
    bytes_rem = 3;
490
0
  }
491
492
  // Parse strings out of the file.
493
0
  int output_size = 0;
494
0
  std::vector<std::string> strings;
495
0
  std::string s;
496
0
  while ((!limit_count || strings.size() < limit_count) &&
497
0
         (limit_input < 0 || static_cast<int>(fin.tellg()) < limit_input) &&
498
0
         fin) {
499
0
    std::string current_str;
500
501
0
    int c = fin.get();
502
0
    for (unsigned int i = 0; i < bytes_rem; ++i) {
503
0
      int c1 = fin.get();
504
0
      if (!fin) {
505
0
        fin.putback(static_cast<char>(c1));
506
0
        break;
507
0
      }
508
0
      c = (c << 8) | c1;
509
0
    }
510
0
    if (encoding == encoding_utf16le) {
511
0
      c = ((c & 0xFF) << 8) | ((c & 0xFF00) >> 8);
512
0
    } else if (encoding == encoding_utf32le) {
513
0
      c = (((c & 0xFF) << 24) | ((c & 0xFF00) << 8) | ((c & 0xFF0000) >> 8) |
514
0
           ((c & 0xFF000000) >> 24));
515
0
    }
516
517
0
    if (c == '\r') {
518
      // Ignore CR character to make output always have UNIX newlines.
519
0
      continue;
520
0
    }
521
522
0
    if (c >= 0 && c <= 0xFF &&
523
0
        (cmsysString_isprint(static_cast<char>(c)) || c == '\t' ||
524
0
         (c == '\n' && newline_consume))) {
525
      // This is an ASCII character that may be part of a string.
526
      // Cast added to avoid compiler warning. Cast is ok because
527
      // c is guaranteed to fit in char by the above if...
528
0
      current_str += static_cast<char>(c);
529
0
    } else if (encoding == encoding_utf8) {
530
      // Check for UTF-8 encoded string (up to 4 octets)
531
0
      static unsigned char const utf8_check_table[3][2] = {
532
0
        { 0xE0, 0xC0 },
533
0
        { 0xF0, 0xE0 },
534
0
        { 0xF8, 0xF0 },
535
0
      };
536
537
      // how many octets are there?
538
0
      unsigned int num_utf8_bytes = 0;
539
0
      for (unsigned int j = 0; num_utf8_bytes == 0 && j < 3; j++) {
540
0
        if ((c & utf8_check_table[j][0]) == utf8_check_table[j][1]) {
541
0
          num_utf8_bytes = j + 2;
542
0
        }
543
0
      }
544
545
      // get subsequent octets and check that they are valid
546
0
      for (unsigned int j = 0; j < num_utf8_bytes; j++) {
547
0
        if (j != 0) {
548
0
          c = fin.get();
549
0
          if (!fin || (c & 0xC0) != 0x80) {
550
0
            fin.putback(static_cast<char>(c));
551
0
            break;
552
0
          }
553
0
        }
554
0
        current_str += static_cast<char>(c);
555
0
      }
556
557
      // if this was an invalid utf8 sequence, discard the data, and put
558
      // back subsequent characters
559
0
      if ((current_str.length() != num_utf8_bytes)) {
560
0
        for (unsigned int j = 0; j < current_str.size() - 1; j++) {
561
0
          fin.putback(current_str[current_str.size() - 1 - j]);
562
0
        }
563
0
        current_str.clear();
564
0
      }
565
0
    }
566
567
0
    if (c == '\n' && !newline_consume) {
568
      // The current line has been terminated.  Check if the current
569
      // string matches the requirements.  The length may now be as
570
      // low as zero since blank lines are allowed.
571
0
      if (s.length() >= minlen && (!have_regex || regex.find(s))) {
572
0
        if (store_regex) {
573
0
          status.GetMakefile().ClearMatches();
574
0
          status.GetMakefile().StoreMatches(regex);
575
0
        }
576
0
        output_size += static_cast<int>(s.size()) + 1;
577
0
        if (limit_output >= 0 && output_size >= limit_output) {
578
0
          s.clear();
579
0
          break;
580
0
        }
581
0
        strings.push_back(s);
582
0
      }
583
584
      // Reset the string to empty.
585
0
      s.clear();
586
0
    } else if (current_str.empty()) {
587
      // A non-string character has been found.  Check if the current
588
      // string matches the requirements.  We require that the length
589
      // be at least one no matter what the user specified.
590
0
      if (s.length() >= minlen && !s.empty() &&
591
0
          (!have_regex || regex.find(s))) {
592
0
        if (store_regex) {
593
0
          status.GetMakefile().ClearMatches();
594
0
          status.GetMakefile().StoreMatches(regex);
595
0
        }
596
0
        output_size += static_cast<int>(s.size()) + 1;
597
0
        if (limit_output >= 0 && output_size >= limit_output) {
598
0
          s.clear();
599
0
          break;
600
0
        }
601
0
        strings.push_back(s);
602
0
      }
603
604
      // Reset the string to empty.
605
0
      s.clear();
606
0
    } else {
607
0
      s += current_str;
608
0
    }
609
610
0
    if (maxlen > 0 && s.size() == maxlen) {
611
      // Terminate a string if the maximum length is reached.
612
0
      if (s.length() >= minlen && (!have_regex || regex.find(s))) {
613
0
        if (store_regex) {
614
0
          status.GetMakefile().ClearMatches();
615
0
          status.GetMakefile().StoreMatches(regex);
616
0
        }
617
0
        output_size += static_cast<int>(s.size()) + 1;
618
0
        if (limit_output >= 0 && output_size >= limit_output) {
619
0
          s.clear();
620
0
          break;
621
0
        }
622
0
        strings.push_back(s);
623
0
      }
624
0
      s.clear();
625
0
    }
626
0
  }
627
628
  // If there is a non-empty current string we have hit the end of the
629
  // input file or the input size limit.  Check if the current string
630
  // matches the requirements.
631
0
  if ((!limit_count || strings.size() < limit_count) && !s.empty() &&
632
0
      s.length() >= minlen && (!have_regex || regex.find(s))) {
633
0
    if (store_regex) {
634
0
      status.GetMakefile().ClearMatches();
635
0
      status.GetMakefile().StoreMatches(regex);
636
0
    }
637
0
    output_size += static_cast<int>(s.size()) + 1;
638
0
    if (limit_output < 0 || output_size < limit_output) {
639
0
      strings.push_back(s);
640
0
    }
641
0
  }
642
643
  // Encode the result in a CMake list.
644
0
  char const* sep = "";
645
0
  std::string output;
646
0
  for (std::string const& sr : strings) {
647
    // Separate the strings in the output to make it a list.
648
0
    output += sep;
649
0
    sep = ";";
650
651
    // Store the string in the output, but escape semicolons to
652
    // make sure it is a list.
653
0
    for (char i : sr) {
654
0
      if (i == ';') {
655
0
        output += '\\';
656
0
      }
657
0
      output += i;
658
0
    }
659
0
  }
660
661
  // Save the output in a makefile variable.
662
0
  status.GetMakefile().AddDefinition(outVar, output);
663
0
  return true;
664
0
}
665
666
bool HandleGlobImpl(std::vector<std::string> const& args, bool recurse,
667
                    cmExecutionStatus& status)
668
0
{
669
0
  if (args.size() < 2) {
670
0
    status.SetError(cmStrCat(
671
0
      args[0], " must be called with at least one additional argument."));
672
0
    return false;
673
0
  }
674
675
0
  auto i = args.begin();
676
677
0
  i++; // Get rid of subcommand
678
679
0
  std::string variable = *i;
680
0
  i++;
681
0
  cmsys::Glob g;
682
0
  g.SetRecurse(recurse);
683
0
  if (recurse) {
684
0
    g.RecurseThroughSymlinksOff();
685
0
  }
686
687
0
  cmake* cm = status.GetMakefile().GetCMakeInstance();
688
0
  std::vector<std::string> files;
689
0
  bool configureDepends = false;
690
0
  bool warnConfigureLate = false;
691
0
  while (i != args.end()) {
692
0
    if (*i == "LIST_DIRECTORIES") {
693
0
      ++i; // skip LIST_DIRECTORIES
694
0
      if (i != args.end()) {
695
0
        if (cmIsOn(*i)) {
696
0
          g.SetListDirs(true);
697
0
          g.SetRecurseListDirs(true);
698
0
        } else if (cmIsOff(*i)) {
699
0
          g.SetListDirs(false);
700
0
          g.SetRecurseListDirs(false);
701
0
        } else {
702
0
          status.SetError("LIST_DIRECTORIES missing bool value.");
703
0
          return false;
704
0
        }
705
0
        ++i;
706
0
      } else {
707
0
        status.SetError("LIST_DIRECTORIES missing bool value.");
708
0
        return false;
709
0
      }
710
0
    } else if (*i == "FOLLOW_SYMLINKS") {
711
0
      ++i; // skip FOLLOW_SYMLINKS
712
0
      if (recurse) {
713
0
        g.RecurseThroughSymlinksOn();
714
0
        if (i == args.end()) {
715
0
          status.SetError(
716
0
            "GLOB_RECURSE requires a glob expression after FOLLOW_SYMLINKS.");
717
0
          return false;
718
0
        }
719
0
      }
720
0
    } else if (*i == "RELATIVE") {
721
0
      ++i; // skip RELATIVE
722
0
      if (i == args.end()) {
723
0
        status.SetError("GLOB requires a directory after the RELATIVE tag.");
724
0
        return false;
725
0
      }
726
0
      g.SetRelative(i->c_str());
727
0
      ++i;
728
0
      if (i == args.end()) {
729
0
        status.SetError(
730
0
          "GLOB requires a glob expression after the directory.");
731
0
        return false;
732
0
      }
733
0
    } else if (*i == "CONFIGURE_DEPENDS") {
734
      // Generated build system depends on glob results
735
0
      if (!configureDepends && warnConfigureLate) {
736
0
        status.GetMakefile().IssueDiagnostic(
737
0
          cmDiagnostics::CMD_AUTHOR,
738
0
          "CONFIGURE_DEPENDS flag was given after a glob expression was "
739
0
          "already evaluated.");
740
0
      }
741
0
      if (cm->GetState()->GetRole() != cmState::Role::Project) {
742
0
        status.GetMakefile().IssueMessage(
743
0
          MessageType::FATAL_ERROR,
744
0
          "CONFIGURE_DEPENDS is invalid for script and find package modes.");
745
0
        return false;
746
0
      }
747
0
      configureDepends = true;
748
0
      ++i;
749
0
      if (i == args.end()) {
750
0
        status.SetError(
751
0
          "GLOB requires a glob expression after CONFIGURE_DEPENDS.");
752
0
        return false;
753
0
      }
754
0
    } else {
755
0
      std::string expr = *i;
756
0
      if (!cmsys::SystemTools::FileIsFullPath(*i)) {
757
0
        expr = status.GetMakefile().GetCurrentSourceDirectory();
758
        // Handle script mode
759
0
        if (!expr.empty()) {
760
0
          expr += "/" + *i;
761
0
        } else {
762
0
          expr = *i;
763
0
        }
764
0
      }
765
766
0
      cmsys::Glob::GlobMessages globMessages;
767
0
      g.FindFiles(expr, &globMessages);
768
769
0
      if (!globMessages.empty()) {
770
0
        bool shouldExit = false;
771
0
        for (cmsys::Glob::Message const& globMessage : globMessages) {
772
0
          if (globMessage.type == cmsys::Glob::cyclicRecursion) {
773
0
            status.GetMakefile().IssueDiagnostic(
774
0
              cmDiagnostics::CMD_AUTHOR,
775
0
              cmStrCat("Cyclic recursion detected while globbing for '", *i,
776
0
                       "':\n", globMessage.content));
777
0
          } else if (globMessage.type == cmsys::Glob::error) {
778
0
            status.GetMakefile().IssueMessage(
779
0
              MessageType::FATAL_ERROR,
780
0
              cmStrCat("Error has occurred while globbing for '", *i, "' - ",
781
0
                       globMessage.content));
782
0
            shouldExit = true;
783
0
          } else if (cm->GetDebugOutput() || cm->GetTrace()) {
784
0
            status.GetMakefile().IssueMessage(
785
0
              MessageType::LOG,
786
0
              cmStrCat("Globbing for\n  ", *i, "\nEncountered an error:\n ",
787
0
                       globMessage.content));
788
0
          }
789
0
        }
790
0
        if (shouldExit) {
791
0
          return false;
792
0
        }
793
0
      }
794
795
0
      std::vector<std::string>& foundFiles = g.GetFiles();
796
0
      cm::append(files, foundFiles);
797
798
0
      if (configureDepends) {
799
0
        std::sort(foundFiles.begin(), foundFiles.end());
800
0
        foundFiles.erase(std::unique(foundFiles.begin(), foundFiles.end()),
801
0
                         foundFiles.end());
802
0
        auto entry = cmGlobCacheEntry{
803
0
          recurse,
804
0
          (recurse ? g.GetRecurseListDirs() : g.GetListDirs()),
805
0
          (recurse ? g.GetRecurseThroughSymlinks() : false),
806
0
          (g.GetRelative() ? g.GetRelative() : ""),
807
0
          expr,
808
0
          foundFiles
809
0
        };
810
0
        cm->AddGlobCacheEntry(entry, variable,
811
0
                              status.GetMakefile().GetBacktrace());
812
0
      } else {
813
0
        warnConfigureLate = true;
814
0
      }
815
0
      ++i;
816
0
    }
817
0
  }
818
819
0
  std::sort(files.begin(), files.end());
820
0
  files.erase(std::unique(files.begin(), files.end()), files.end());
821
0
  status.GetMakefile().AddDefinition(variable, cmList::to_string(files));
822
0
  return true;
823
0
}
824
825
bool HandleGlobCommand(std::vector<std::string> const& args,
826
                       cmExecutionStatus& status)
827
0
{
828
0
  return HandleGlobImpl(args, false, status);
829
0
}
830
831
bool HandleGlobRecurseCommand(std::vector<std::string> const& args,
832
                              cmExecutionStatus& status)
833
0
{
834
0
  return HandleGlobImpl(args, true, status);
835
0
}
836
837
bool HandleMakeDirectoryCommand(std::vector<std::string> const& args,
838
                                cmExecutionStatus& status)
839
0
{
840
  // Projects might pass a dynamically generated list of directories, and it
841
  // could be an empty list. We should not assume there is at least one.
842
843
0
  cmRange<std::vector<std::string>::const_iterator> argsRange =
844
0
    cmMakeRange(args).advance(1); // Get rid of subcommand
845
846
0
  struct Arguments : public ArgumentParser::ParseResult
847
0
  {
848
0
    std::string Result;
849
0
  };
850
0
  Arguments arguments;
851
852
0
  auto resultPosItr =
853
0
    std::find(cm::begin(argsRange), cm::end(argsRange), "RESULT");
854
0
  if (resultPosItr != cm::end(argsRange)) {
855
0
    static auto const parser =
856
0
      cmArgumentParser<Arguments>{}.Bind("RESULT"_s, &Arguments::Result);
857
0
    std::vector<std::string> unparsedArguments;
858
0
    auto resultDistanceFromBegin =
859
0
      std::distance(cm::begin(argsRange), resultPosItr);
860
0
    arguments =
861
0
      parser.Parse(cmMakeRange(argsRange).advance(resultDistanceFromBegin),
862
0
                   &unparsedArguments);
863
864
0
    if (!unparsedArguments.empty()) {
865
0
      std::string unexpectedArgsStr = cmJoin(
866
0
        cmMakeRange(cm::begin(unparsedArguments), cm::end(unparsedArguments)),
867
0
        "\n");
868
0
      status.SetError("MAKE_DIRECTORY called with unexpected\n"
869
0
                      "arguments:\n  " +
870
0
                      unexpectedArgsStr);
871
0
      return false;
872
0
    }
873
874
0
    auto resultDistanceFromEnd =
875
0
      std::distance(cm::end(argsRange), resultPosItr);
876
0
    argsRange = argsRange.retreat(-resultDistanceFromEnd);
877
0
  }
878
879
0
  std::string expr;
880
0
  for (std::string const& arg : argsRange) {
881
0
    std::string const* cdir = &arg;
882
0
    if (!cmsys::SystemTools::FileIsFullPath(arg)) {
883
0
      expr =
884
0
        cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', arg);
885
0
      cdir = &expr;
886
0
    }
887
0
    if (!status.GetMakefile().CanIWriteThisFile(*cdir)) {
888
0
      std::string e = cmStrCat("attempted to create a directory: ", *cdir,
889
0
                               " into a source directory.");
890
0
      if (arguments.Result.empty()) {
891
0
        status.SetError(e);
892
0
        cmSystemTools::SetFatalErrorOccurred();
893
0
        return false;
894
0
      }
895
0
      status.GetMakefile().AddDefinition(arguments.Result, e);
896
0
      return true;
897
0
    }
898
0
    cmsys::Status mkdirStatus = cmSystemTools::MakeDirectory(*cdir);
899
0
    if (!mkdirStatus) {
900
0
      if (arguments.Result.empty()) {
901
0
        std::string errorOutput =
902
0
          cmStrCat("failed to create directory:\n  ", *cdir,
903
0
                   "\nbecause: ", mkdirStatus.GetString());
904
0
        status.SetError(errorOutput);
905
0
        return false;
906
0
      }
907
0
      std::string errorResult = cmStrCat("Failed to create directory: ", *cdir,
908
0
                                         " Error: ", mkdirStatus.GetString());
909
0
      status.GetMakefile().AddDefinition(arguments.Result, errorResult);
910
0
      return true;
911
0
    }
912
0
  }
913
0
  if (!arguments.Result.empty()) {
914
0
    status.GetMakefile().AddDefinition(arguments.Result, "0");
915
0
  }
916
0
  return true;
917
0
}
918
919
bool HandleTouchImpl(std::vector<std::string> const& args, bool create,
920
                     cmExecutionStatus& status)
921
0
{
922
  // Projects might pass a dynamically generated list of files, and it
923
  // could be an empty list. We should not assume there is at least one.
924
925
0
  for (std::string const& arg :
926
0
       cmMakeRange(args).advance(1)) // Get rid of subcommand
927
0
  {
928
0
    std::string tfile = arg;
929
0
    if (!cmsys::SystemTools::FileIsFullPath(tfile)) {
930
0
      tfile =
931
0
        cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', arg);
932
0
    }
933
0
    if (!status.GetMakefile().CanIWriteThisFile(tfile)) {
934
0
      std::string e = cmStrCat("attempted to touch a file: ", tfile,
935
0
                               " in a source directory.");
936
0
      status.SetError(e);
937
0
      cmSystemTools::SetFatalErrorOccurred();
938
0
      return false;
939
0
    }
940
0
    if (!cmSystemTools::Touch(tfile, create)) {
941
0
      std::string error = "problem touching file: " + tfile;
942
0
      status.SetError(error);
943
0
      return false;
944
0
    }
945
0
  }
946
0
  return true;
947
0
}
948
949
bool HandleTouchCommand(std::vector<std::string> const& args,
950
                        cmExecutionStatus& status)
951
0
{
952
0
  return HandleTouchImpl(args, true, status);
953
0
}
954
955
bool HandleTouchNocreateCommand(std::vector<std::string> const& args,
956
                                cmExecutionStatus& status)
957
0
{
958
0
  return HandleTouchImpl(args, false, status);
959
0
}
960
961
bool HandleDifferentCommand(std::vector<std::string> const& args,
962
                            cmExecutionStatus& status)
963
0
{
964
  /*
965
    FILE(DIFFERENT <variable> FILES <lhs> <rhs>)
966
   */
967
968
  // Evaluate arguments.
969
0
  char const* file_lhs = nullptr;
970
0
  char const* file_rhs = nullptr;
971
0
  char const* var = nullptr;
972
0
  enum Doing
973
0
  {
974
0
    DoingNone,
975
0
    DoingVar,
976
0
    DoingFileLHS,
977
0
    DoingFileRHS
978
0
  };
979
0
  Doing doing = DoingVar;
980
0
  for (unsigned int i = 1; i < args.size(); ++i) {
981
0
    if (args[i] == "FILES") {
982
0
      doing = DoingFileLHS;
983
0
    } else if (doing == DoingVar) {
984
0
      var = args[i].c_str();
985
0
      doing = DoingNone;
986
0
    } else if (doing == DoingFileLHS) {
987
0
      file_lhs = args[i].c_str();
988
0
      doing = DoingFileRHS;
989
0
    } else if (doing == DoingFileRHS) {
990
0
      file_rhs = args[i].c_str();
991
0
      doing = DoingNone;
992
0
    } else {
993
0
      status.SetError(cmStrCat("DIFFERENT given unknown argument ", args[i]));
994
0
      return false;
995
0
    }
996
0
  }
997
0
  if (!var) {
998
0
    status.SetError("DIFFERENT not given result variable name.");
999
0
    return false;
1000
0
  }
1001
0
  if (!file_lhs || !file_rhs) {
1002
0
    status.SetError("DIFFERENT not given FILES option with two file names.");
1003
0
    return false;
1004
0
  }
1005
1006
  // Compare the files.
1007
0
  char const* result =
1008
0
    cmSystemTools::FilesDiffer(file_lhs, file_rhs) ? "1" : "0";
1009
0
  status.GetMakefile().AddDefinition(var, result);
1010
0
  return true;
1011
0
}
1012
1013
bool HandleCopyCommand(std::vector<std::string> const& args,
1014
                       cmExecutionStatus& status)
1015
0
{
1016
0
  cmFileCopier copier(status);
1017
0
  return copier.Run(args);
1018
0
}
1019
1020
bool HandleRPathChangeCommand(std::vector<std::string> const& args,
1021
                              cmExecutionStatus& status)
1022
0
{
1023
  // Evaluate arguments.
1024
0
  std::string file;
1025
0
  cm::optional<std::string> oldRPath;
1026
0
  cm::optional<std::string> newRPath;
1027
0
  bool removeEnvironmentRPath = false;
1028
0
  cmArgumentParser<void> parser;
1029
0
  std::vector<std::string> unknownArgs;
1030
0
  parser.Bind("FILE"_s, file)
1031
0
    .Bind("OLD_RPATH"_s, oldRPath)
1032
0
    .Bind("NEW_RPATH"_s, newRPath)
1033
0
    .Bind("INSTALL_REMOVE_ENVIRONMENT_RPATH"_s, removeEnvironmentRPath);
1034
0
  ArgumentParser::ParseResult parseResult =
1035
0
    parser.Parse(cmMakeRange(args).advance(1), &unknownArgs);
1036
0
  if (!unknownArgs.empty()) {
1037
0
    status.SetError(
1038
0
      cmStrCat("RPATH_CHANGE given unknown argument ", unknownArgs.front()));
1039
0
    return false;
1040
0
  }
1041
0
  if (parseResult.MaybeReportError(status.GetMakefile())) {
1042
0
    return true;
1043
0
  }
1044
0
  if (file.empty()) {
1045
0
    status.SetError("RPATH_CHANGE not given FILE option.");
1046
0
    return false;
1047
0
  }
1048
0
  if (!oldRPath) {
1049
0
    status.SetError("RPATH_CHANGE not given OLD_RPATH option.");
1050
0
    return false;
1051
0
  }
1052
0
  if (!newRPath) {
1053
0
    status.SetError("RPATH_CHANGE not given NEW_RPATH option.");
1054
0
    return false;
1055
0
  }
1056
0
  if (!cmSystemTools::FileExists(file, true)) {
1057
0
    status.SetError(
1058
0
      cmStrCat("RPATH_CHANGE given FILE \"", file, "\" that does not exist."));
1059
0
    return false;
1060
0
  }
1061
0
  bool success = true;
1062
0
  cmFileTimes const ft(file);
1063
0
  std::string emsg;
1064
0
  bool changed;
1065
1066
0
  if (!cmSystemTools::ChangeRPath(file, *oldRPath, *newRPath,
1067
0
                                  removeEnvironmentRPath, &emsg, &changed)) {
1068
0
    status.SetError(cmStrCat("RPATH_CHANGE could not write new RPATH:\n  ",
1069
0
                             *newRPath, "\nto the file:\n  ", file, '\n',
1070
0
                             emsg));
1071
0
    success = false;
1072
0
  }
1073
0
  if (success) {
1074
0
    if (changed) {
1075
0
      std::string message =
1076
0
        cmStrCat("Set non-toolchain portion of runtime path of \"", file,
1077
0
                 "\" to \"", *newRPath, '"');
1078
0
      status.GetMakefile().DisplayStatus(message, -1);
1079
0
    }
1080
0
    ft.Store(file);
1081
0
  }
1082
0
  return success;
1083
0
}
1084
1085
bool HandleRPathSetCommand(std::vector<std::string> const& args,
1086
                           cmExecutionStatus& status)
1087
0
{
1088
  // Evaluate arguments.
1089
0
  std::string file;
1090
0
  cm::optional<std::string> newRPath;
1091
0
  cmArgumentParser<void> parser;
1092
0
  std::vector<std::string> unknownArgs;
1093
0
  parser.Bind("FILE"_s, file).Bind("NEW_RPATH"_s, newRPath);
1094
0
  ArgumentParser::ParseResult parseResult =
1095
0
    parser.Parse(cmMakeRange(args).advance(1), &unknownArgs);
1096
0
  if (!unknownArgs.empty()) {
1097
0
    status.SetError(cmStrCat("RPATH_SET given unrecognized argument \"",
1098
0
                             unknownArgs.front(), "\"."));
1099
0
    return false;
1100
0
  }
1101
0
  if (parseResult.MaybeReportError(status.GetMakefile())) {
1102
0
    return true;
1103
0
  }
1104
0
  if (file.empty()) {
1105
0
    status.SetError("RPATH_SET not given FILE option.");
1106
0
    return false;
1107
0
  }
1108
0
  if (!newRPath) {
1109
0
    status.SetError("RPATH_SET not given NEW_RPATH option.");
1110
0
    return false;
1111
0
  }
1112
0
  if (!cmSystemTools::FileExists(file, true)) {
1113
0
    status.SetError(
1114
0
      cmStrCat("RPATH_SET given FILE \"", file, "\" that does not exist."));
1115
0
    return false;
1116
0
  }
1117
0
  bool success = true;
1118
0
  cmFileTimes const ft(file);
1119
0
  std::string emsg;
1120
0
  bool changed;
1121
1122
0
  if (!cmSystemTools::SetRPath(file, *newRPath, &emsg, &changed)) {
1123
0
    status.SetError(cmStrCat("RPATH_SET could not write new RPATH:\n  ",
1124
0
                             *newRPath, "\nto the file:\n  ", file, '\n',
1125
0
                             emsg));
1126
0
    success = false;
1127
0
  }
1128
0
  if (success) {
1129
0
    if (changed) {
1130
0
      std::string message =
1131
0
        cmStrCat("Set non-toolchain portion of runtime path of \"", file,
1132
0
                 "\" to \"", *newRPath, '"');
1133
0
      status.GetMakefile().DisplayStatus(message, -1);
1134
0
    }
1135
0
    ft.Store(file);
1136
0
  }
1137
0
  return success;
1138
0
}
1139
1140
bool HandleRPathRemoveCommand(std::vector<std::string> const& args,
1141
                              cmExecutionStatus& status)
1142
0
{
1143
  // Evaluate arguments.
1144
0
  std::string file;
1145
0
  cmArgumentParser<void> parser;
1146
0
  std::vector<std::string> unknownArgs;
1147
0
  parser.Bind("FILE"_s, file);
1148
0
  ArgumentParser::ParseResult parseResult =
1149
0
    parser.Parse(cmMakeRange(args).advance(1), &unknownArgs);
1150
0
  if (!unknownArgs.empty()) {
1151
0
    status.SetError(
1152
0
      cmStrCat("RPATH_REMOVE given unknown argument ", unknownArgs.front()));
1153
0
    return false;
1154
0
  }
1155
0
  if (parseResult.MaybeReportError(status.GetMakefile())) {
1156
0
    return true;
1157
0
  }
1158
0
  if (file.empty()) {
1159
0
    status.SetError("RPATH_REMOVE not given FILE option.");
1160
0
    return false;
1161
0
  }
1162
0
  if (!cmSystemTools::FileExists(file, true)) {
1163
0
    status.SetError(
1164
0
      cmStrCat("RPATH_REMOVE given FILE \"", file, "\" that does not exist."));
1165
0
    return false;
1166
0
  }
1167
0
  bool success = true;
1168
0
  cmFileTimes const ft(file);
1169
0
  std::string emsg;
1170
0
  bool removed;
1171
0
  if (!cmSystemTools::RemoveRPath(file, &emsg, &removed)) {
1172
0
    status.SetError(
1173
0
      cmStrCat("RPATH_REMOVE could not remove RPATH from file: \n  ", file,
1174
0
               '\n', emsg));
1175
0
    success = false;
1176
0
  }
1177
0
  if (success) {
1178
0
    if (removed) {
1179
0
      std::string message =
1180
0
        cmStrCat("Removed runtime path from \"", file, '"');
1181
0
      status.GetMakefile().DisplayStatus(message, -1);
1182
0
    }
1183
0
    ft.Store(file);
1184
0
  }
1185
0
  return success;
1186
0
}
1187
1188
bool HandleRPathCheckCommand(std::vector<std::string> const& args,
1189
                             cmExecutionStatus& status)
1190
0
{
1191
  // Evaluate arguments.
1192
0
  std::string file;
1193
0
  cm::optional<std::string> rpath;
1194
0
  cmArgumentParser<void> parser;
1195
0
  std::vector<std::string> unknownArgs;
1196
0
  parser.Bind("FILE"_s, file).Bind("RPATH"_s, rpath);
1197
0
  ArgumentParser::ParseResult parseResult =
1198
0
    parser.Parse(cmMakeRange(args).advance(1), &unknownArgs);
1199
0
  if (!unknownArgs.empty()) {
1200
0
    status.SetError(
1201
0
      cmStrCat("RPATH_CHECK given unknown argument ", unknownArgs.front()));
1202
0
    return false;
1203
0
  }
1204
0
  if (parseResult.MaybeReportError(status.GetMakefile())) {
1205
0
    return true;
1206
0
  }
1207
0
  if (file.empty()) {
1208
0
    status.SetError("RPATH_CHECK not given FILE option.");
1209
0
    return false;
1210
0
  }
1211
0
  if (!rpath) {
1212
0
    status.SetError("RPATH_CHECK not given RPATH option.");
1213
0
    return false;
1214
0
  }
1215
1216
  // If the file exists but does not have the desired RPath then
1217
  // delete it.  This is used during installation to re-install a file
1218
  // if its RPath will change.
1219
0
  if (cmSystemTools::FileExists(file, true) &&
1220
0
      !cmSystemTools::CheckRPath(file, *rpath)) {
1221
0
    cmSystemTools::RemoveFile(file);
1222
0
  }
1223
1224
0
  return true;
1225
0
}
1226
1227
bool HandleReadElfCommand(std::vector<std::string> const& args,
1228
                          cmExecutionStatus& status)
1229
0
{
1230
0
  if (args.size() < 4) {
1231
0
    status.SetError("READ_ELF must be called with at least three additional "
1232
0
                    "arguments.");
1233
0
    return false;
1234
0
  }
1235
1236
0
  std::string const& fileNameArg = args[1];
1237
1238
0
  struct Arguments
1239
0
  {
1240
0
    std::string RPath;
1241
0
    std::string RunPath;
1242
0
    std::string Error;
1243
0
  };
1244
1245
0
  static auto const parser = cmArgumentParser<Arguments>{}
1246
0
                               .Bind("RPATH"_s, &Arguments::RPath)
1247
0
                               .Bind("RUNPATH"_s, &Arguments::RunPath)
1248
0
                               .Bind("CAPTURE_ERROR"_s, &Arguments::Error);
1249
0
  Arguments const arguments = parser.Parse(cmMakeRange(args).advance(2),
1250
0
                                           /*unparsedArguments=*/nullptr);
1251
1252
0
  if (!cmSystemTools::FileExists(fileNameArg, true)) {
1253
0
    status.SetError(cmStrCat("READ_ELF given FILE \"", fileNameArg,
1254
0
                             "\" that does not exist."));
1255
0
    return false;
1256
0
  }
1257
1258
0
  cmELF elf(fileNameArg.c_str());
1259
0
  if (!elf) {
1260
0
    if (arguments.Error.empty()) {
1261
0
      status.SetError(cmStrCat("READ_ELF given FILE:\n  ", fileNameArg,
1262
0
                               "\nthat is not a valid ELF file."));
1263
0
      return false;
1264
0
    }
1265
0
    status.GetMakefile().AddDefinition(arguments.Error,
1266
0
                                       "not a valid ELF file");
1267
0
    return true;
1268
0
  }
1269
1270
0
  if (!arguments.RPath.empty()) {
1271
0
    if (cmELF::StringEntry const* se_rpath = elf.GetRPath()) {
1272
0
      std::string rpath(se_rpath->Value);
1273
0
      std::replace(rpath.begin(), rpath.end(), ':', ';');
1274
0
      status.GetMakefile().AddDefinition(arguments.RPath, rpath);
1275
0
    }
1276
0
  }
1277
0
  if (!arguments.RunPath.empty()) {
1278
0
    if (cmELF::StringEntry const* se_runpath = elf.GetRunPath()) {
1279
0
      std::string runpath(se_runpath->Value);
1280
0
      std::replace(runpath.begin(), runpath.end(), ':', ';');
1281
0
      status.GetMakefile().AddDefinition(arguments.RunPath, runpath);
1282
0
    }
1283
0
  }
1284
1285
0
  return true;
1286
0
}
1287
1288
bool HandleInstallCommand(std::vector<std::string> const& args,
1289
                          cmExecutionStatus& status)
1290
0
{
1291
0
  cmFileInstaller installer(status);
1292
0
  return installer.Run(args);
1293
0
}
1294
1295
bool HandleRealPathCommand(std::vector<std::string> const& args,
1296
                           cmExecutionStatus& status)
1297
0
{
1298
0
  if (args.size() < 3) {
1299
0
    status.SetError("REAL_PATH requires a path and an output variable");
1300
0
    return false;
1301
0
  }
1302
1303
0
  struct Arguments : public ArgumentParser::ParseResult
1304
0
  {
1305
0
    cm::optional<std::string> BaseDirectory;
1306
0
    bool ExpandTilde = false;
1307
0
  };
1308
0
  static auto const parser =
1309
0
    cmArgumentParser<Arguments>{}
1310
0
      .Bind("BASE_DIRECTORY"_s, &Arguments::BaseDirectory)
1311
0
      .Bind("EXPAND_TILDE"_s, &Arguments::ExpandTilde);
1312
1313
0
  std::vector<std::string> unparsedArguments;
1314
0
  auto arguments =
1315
0
    parser.Parse(cmMakeRange(args).advance(3), &unparsedArguments);
1316
1317
0
  if (!unparsedArguments.empty()) {
1318
0
    status.SetError("REAL_PATH called with unexpected arguments");
1319
0
    return false;
1320
0
  }
1321
0
  if (arguments.MaybeReportError(status.GetMakefile())) {
1322
0
    return true;
1323
0
  }
1324
1325
0
  if (!arguments.BaseDirectory) {
1326
0
    arguments.BaseDirectory = status.GetMakefile().GetCurrentSourceDirectory();
1327
0
  }
1328
1329
0
  auto input = args[1];
1330
0
  if (arguments.ExpandTilde && !input.empty()) {
1331
0
    if (input[0] == '~' && (input.length() == 1 || input[1] == '/')) {
1332
0
      std::string home;
1333
0
      if (
1334
#if defined(_WIN32) && !defined(__CYGWIN__)
1335
        cmSystemTools::GetEnv("USERPROFILE", home) ||
1336
#endif
1337
0
        cmSystemTools::GetEnv("HOME", home)) {
1338
0
        input.replace(0, 1, home);
1339
0
      }
1340
0
    }
1341
0
  }
1342
1343
0
  bool warnAbout152 = false;
1344
0
  bool use152New = true;
1345
0
  cmPolicies::PolicyStatus policyStatus =
1346
0
    status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0152);
1347
0
  switch (policyStatus) {
1348
0
    case cmPolicies::NEW:
1349
0
      break;
1350
0
    case cmPolicies::WARN:
1351
0
      use152New = false;
1352
0
      warnAbout152 = true;
1353
0
      break;
1354
0
    case cmPolicies::OLD:
1355
0
      use152New = false;
1356
0
      warnAbout152 = false;
1357
0
      break;
1358
0
  }
1359
1360
0
  auto computeNewPath = [=](std::string const& in, std::string& result) {
1361
0
    auto path = cmCMakePath{ in };
1362
0
    if (path.IsRelative()) {
1363
0
      auto basePath = cmCMakePath{ *arguments.BaseDirectory };
1364
0
      path = basePath.Append(path);
1365
0
    }
1366
0
    result = cmSystemTools::GetRealPath(path.String());
1367
0
  };
1368
1369
0
  std::string realPath;
1370
0
  if (use152New) {
1371
0
    computeNewPath(input, realPath);
1372
0
  } else {
1373
0
    std::string oldPolicyPath =
1374
0
      cmSystemTools::CollapseFullPath(input, *arguments.BaseDirectory);
1375
0
    oldPolicyPath = cmSystemTools::GetRealPath(oldPolicyPath);
1376
0
    if (warnAbout152) {
1377
0
      computeNewPath(input, realPath);
1378
0
      if (oldPolicyPath != realPath) {
1379
0
        status.GetMakefile().IssueDiagnostic(
1380
0
          cmDiagnostics::CMD_AUTHOR,
1381
0
          cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0152),
1382
0
                   "\n"
1383
0
                   "From input path:\n  ",
1384
0
                   input, "\nthe policy OLD behavior produces path:\n  ",
1385
0
                   oldPolicyPath,
1386
0
                   "\nbut the policy NEW behavior produces path:\n  ",
1387
0
                   realPath,
1388
0
                   "\nSince the policy is not set, CMake is using the OLD "
1389
0
                   "behavior for compatibility."));
1390
0
      }
1391
0
    }
1392
0
    realPath = oldPolicyPath;
1393
0
  }
1394
1395
0
  if (!cmSystemTools::FileExists(realPath)) {
1396
0
    status.GetMakefile().IssueDiagnostic(
1397
0
      cmDiagnostics::CMD_AUTHOR,
1398
0
      cmStrCat("Given path:\n  ", input,
1399
0
               "\ndoes not refer to an existing path on disk."));
1400
0
  }
1401
1402
0
  status.GetMakefile().AddDefinition(args[2], realPath);
1403
1404
0
  return true;
1405
0
}
1406
1407
bool HandleRelativePathCommand(std::vector<std::string> const& args,
1408
                               cmExecutionStatus& status)
1409
0
{
1410
0
  if (args.size() != 4) {
1411
0
    status.SetError("RELATIVE_PATH called with incorrect number of arguments");
1412
0
    return false;
1413
0
  }
1414
1415
0
  std::string const& outVar = args[1];
1416
0
  std::string const& directoryName = args[2];
1417
0
  std::string const& fileName = args[3];
1418
1419
0
  if (!cmSystemTools::FileIsFullPath(directoryName)) {
1420
0
    std::string errstring =
1421
0
      "RELATIVE_PATH must be passed a full path to the directory: " +
1422
0
      directoryName;
1423
0
    status.SetError(errstring);
1424
0
    return false;
1425
0
  }
1426
0
  if (!cmSystemTools::FileIsFullPath(fileName)) {
1427
0
    std::string errstring =
1428
0
      "RELATIVE_PATH must be passed a full path to the file: " + fileName;
1429
0
    status.SetError(errstring);
1430
0
    return false;
1431
0
  }
1432
1433
0
  std::string res = cmSystemTools::RelativePath(directoryName, fileName);
1434
0
  status.GetMakefile().AddDefinition(outVar, res);
1435
0
  return true;
1436
0
}
1437
1438
bool HandleRename(std::vector<std::string> const& args,
1439
                  cmExecutionStatus& status)
1440
0
{
1441
0
  if (args.size() < 3) {
1442
0
    status.SetError("RENAME must be called with at least two additional "
1443
0
                    "arguments");
1444
0
    return false;
1445
0
  }
1446
1447
  // Compute full path for old and new names.
1448
0
  std::string oldname = args[1];
1449
0
  if (!cmsys::SystemTools::FileIsFullPath(oldname)) {
1450
0
    oldname =
1451
0
      cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', args[1]);
1452
0
  }
1453
0
  std::string newname = args[2];
1454
0
  if (!cmsys::SystemTools::FileIsFullPath(newname)) {
1455
0
    newname =
1456
0
      cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', args[2]);
1457
0
  }
1458
1459
0
  struct Arguments
1460
0
  {
1461
0
    bool NoReplace = false;
1462
0
    std::string Result;
1463
0
  };
1464
1465
0
  static auto const parser = cmArgumentParser<Arguments>{}
1466
0
                               .Bind("NO_REPLACE"_s, &Arguments::NoReplace)
1467
0
                               .Bind("RESULT"_s, &Arguments::Result);
1468
1469
0
  std::vector<std::string> unconsumedArgs;
1470
0
  Arguments const arguments =
1471
0
    parser.Parse(cmMakeRange(args).advance(3), &unconsumedArgs);
1472
0
  if (!unconsumedArgs.empty()) {
1473
0
    status.SetError("RENAME unknown argument:\n  " + unconsumedArgs.front());
1474
0
    return false;
1475
0
  }
1476
1477
0
  std::string err;
1478
0
  switch (cmSystemTools::RenameFile(oldname, newname,
1479
0
                                    arguments.NoReplace
1480
0
                                      ? cmSystemTools::Replace::No
1481
0
                                      : cmSystemTools::Replace::Yes,
1482
0
                                    &err)) {
1483
0
    case cmSystemTools::RenameResult::Success:
1484
0
      if (!arguments.Result.empty()) {
1485
0
        status.GetMakefile().AddDefinition(arguments.Result, "0");
1486
0
      }
1487
0
      return true;
1488
0
    case cmSystemTools::RenameResult::NoReplace:
1489
0
      if (!arguments.Result.empty()) {
1490
0
        err = "NO_REPLACE";
1491
0
      } else {
1492
0
        err = "path not replaced";
1493
0
      }
1494
0
      CM_FALLTHROUGH;
1495
0
    case cmSystemTools::RenameResult::Failure:
1496
0
      if (!arguments.Result.empty()) {
1497
0
        status.GetMakefile().AddDefinition(arguments.Result, err);
1498
0
        return true;
1499
0
      }
1500
0
      break;
1501
0
  }
1502
0
  status.SetError(cmStrCat("RENAME failed to rename\n  ", oldname, "\nto\n  ",
1503
0
                           newname, "\nbecause: ", err, '\n'));
1504
0
  return false;
1505
0
}
1506
1507
bool HandleCopyFile(std::vector<std::string> const& args,
1508
                    cmExecutionStatus& status)
1509
0
{
1510
0
  if (args.size() < 3) {
1511
0
    status.SetError("COPY_FILE must be called with at least two additional "
1512
0
                    "arguments");
1513
0
    return false;
1514
0
  }
1515
1516
  // Compute full path for old and new names.
1517
0
  std::string oldname = args[1];
1518
0
  if (!cmsys::SystemTools::FileIsFullPath(oldname)) {
1519
0
    oldname =
1520
0
      cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', args[1]);
1521
0
  }
1522
0
  std::string newname = args[2];
1523
0
  if (!cmsys::SystemTools::FileIsFullPath(newname)) {
1524
0
    newname =
1525
0
      cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', args[2]);
1526
0
  }
1527
1528
0
  struct Arguments
1529
0
  {
1530
0
    bool InputMayBeRecent = false;
1531
0
    bool OnlyIfDifferent = false;
1532
0
    std::string Result;
1533
0
  };
1534
1535
0
  static auto const parser =
1536
0
    cmArgumentParser<Arguments>{}
1537
0
      .Bind("INPUT_MAY_BE_RECENT"_s, &Arguments::InputMayBeRecent)
1538
0
      .Bind("ONLY_IF_DIFFERENT"_s, &Arguments::OnlyIfDifferent)
1539
0
      .Bind("RESULT"_s, &Arguments::Result);
1540
1541
0
  std::vector<std::string> unconsumedArgs;
1542
0
  Arguments const arguments =
1543
0
    parser.Parse(cmMakeRange(args).advance(3), &unconsumedArgs);
1544
0
  if (!unconsumedArgs.empty()) {
1545
0
    status.SetError("COPY_FILE unknown argument:\n  " +
1546
0
                    unconsumedArgs.front());
1547
0
    return false;
1548
0
  }
1549
1550
0
  bool result = true;
1551
0
  if (cmsys::SystemTools::FileIsDirectory(oldname)) {
1552
0
    if (!arguments.Result.empty()) {
1553
0
      status.GetMakefile().AddDefinition(arguments.Result,
1554
0
                                         "cannot copy a directory");
1555
0
    } else {
1556
0
      status.SetError(
1557
0
        cmStrCat("COPY_FILE cannot copy a directory\n  ", oldname));
1558
0
      result = false;
1559
0
    }
1560
0
    return result;
1561
0
  }
1562
0
  if (cmsys::SystemTools::FileIsDirectory(newname)) {
1563
0
    if (!arguments.Result.empty()) {
1564
0
      status.GetMakefile().AddDefinition(arguments.Result,
1565
0
                                         "cannot copy to a directory");
1566
0
    } else {
1567
0
      status.SetError(
1568
0
        cmStrCat("COPY_FILE cannot copy to a directory\n  ", newname));
1569
0
      result = false;
1570
0
    }
1571
0
    return result;
1572
0
  }
1573
1574
0
  cmSystemTools::CopyWhen when;
1575
0
  if (arguments.OnlyIfDifferent) {
1576
0
    when = cmSystemTools::CopyWhen::OnlyIfDifferent;
1577
0
  } else {
1578
0
    when = cmSystemTools::CopyWhen::Always;
1579
0
  }
1580
0
  cmSystemTools::CopyInputRecent const inputRecent = arguments.InputMayBeRecent
1581
0
    ? cmSystemTools::CopyInputRecent::Yes
1582
0
    : cmSystemTools::CopyInputRecent::No;
1583
1584
0
  std::string err;
1585
0
  if (cmSystemTools::CopySingleFile(oldname, newname, when, inputRecent,
1586
0
                                    &err) ==
1587
0
      cmSystemTools::CopyResult::Success) {
1588
0
    if (!arguments.Result.empty()) {
1589
0
      status.GetMakefile().AddDefinition(arguments.Result, "0");
1590
0
    }
1591
0
  } else {
1592
0
    if (!arguments.Result.empty()) {
1593
0
      status.GetMakefile().AddDefinition(arguments.Result, err);
1594
0
    } else {
1595
0
      status.SetError(cmStrCat("COPY_FILE failed to copy\n  ", oldname,
1596
0
                               "\nto\n  ", newname, "\nbecause: ", err, '\n'));
1597
0
      result = false;
1598
0
    }
1599
0
  }
1600
1601
0
  return result;
1602
0
}
1603
1604
bool HandleRemoveImpl(std::vector<std::string> const& args, bool recurse,
1605
                      cmExecutionStatus& status)
1606
0
{
1607
0
  for (std::string const& arg :
1608
0
       cmMakeRange(args).advance(1)) // Get rid of subcommand
1609
0
  {
1610
0
    std::string fileName = arg;
1611
0
    if (fileName.empty()) {
1612
0
      std::string r = recurse ? "REMOVE_RECURSE" : "REMOVE";
1613
0
      status.GetMakefile().IssueDiagnostic(
1614
0
        cmDiagnostics::CMD_AUTHOR,
1615
0
        cmStrCat("Ignoring empty file name in ", std::move(r), '.'));
1616
0
      continue;
1617
0
    }
1618
0
    if (!cmsys::SystemTools::FileIsFullPath(fileName)) {
1619
0
      fileName =
1620
0
        cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', arg);
1621
0
    }
1622
1623
0
    if (cmSystemTools::FileIsDirectory(fileName) &&
1624
0
        !cmSystemTools::FileIsSymlink(fileName) && recurse) {
1625
0
      cmSystemTools::RepeatedRemoveDirectory(fileName);
1626
0
    } else {
1627
0
      cmSystemTools::RemoveFile(fileName);
1628
0
    }
1629
0
  }
1630
0
  return true;
1631
0
}
1632
1633
bool HandleRemove(std::vector<std::string> const& args,
1634
                  cmExecutionStatus& status)
1635
0
{
1636
0
  return HandleRemoveImpl(args, false, status);
1637
0
}
1638
1639
bool HandleRemoveRecurse(std::vector<std::string> const& args,
1640
                         cmExecutionStatus& status)
1641
0
{
1642
0
  return HandleRemoveImpl(args, true, status);
1643
0
}
1644
1645
std::string ToNativePath(std::string const& path)
1646
0
{
1647
0
  auto const& outPath = cmSystemTools::ConvertToOutputPath(path);
1648
0
  if (outPath.size() > 1 && outPath.front() == '\"' &&
1649
0
      outPath.back() == '\"') {
1650
0
    return outPath.substr(1, outPath.size() - 2);
1651
0
  }
1652
0
  return outPath;
1653
0
}
1654
1655
std::string ToCMakePath(std::string const& path)
1656
0
{
1657
0
  auto temp = path;
1658
0
  cmSystemTools::ConvertToUnixSlashes(temp);
1659
0
  return temp;
1660
0
}
1661
1662
bool HandlePathCommand(std::vector<std::string> const& args,
1663
                       std::string (*convert)(std::string const&),
1664
                       cmExecutionStatus& status)
1665
0
{
1666
0
  if (args.size() != 3) {
1667
0
    status.SetError("FILE([TO_CMAKE_PATH|TO_NATIVE_PATH] path result) must be "
1668
0
                    "called with exactly three arguments.");
1669
0
    return false;
1670
0
  }
1671
#if defined(_WIN32) && !defined(__CYGWIN__)
1672
  char pathSep = ';';
1673
#else
1674
0
  char pathSep = ':';
1675
0
#endif
1676
0
  std::vector<std::string> path = cmSystemTools::SplitString(args[1], pathSep);
1677
1678
0
  std::string value = cmList::to_string(cmMakeRange(path).transform(convert));
1679
0
  status.GetMakefile().AddDefinition(args[2], value);
1680
0
  return true;
1681
0
}
1682
1683
bool HandleCMakePathCommand(std::vector<std::string> const& args,
1684
                            cmExecutionStatus& status)
1685
0
{
1686
0
  return HandlePathCommand(args, ToCMakePath, status);
1687
0
}
1688
1689
bool HandleNativePathCommand(std::vector<std::string> const& args,
1690
                             cmExecutionStatus& status)
1691
0
{
1692
0
  return HandlePathCommand(args, ToNativePath, status);
1693
0
}
1694
1695
#if !defined(CMAKE_BOOTSTRAP)
1696
1697
bool const TLS_VERIFY_DEFAULT = true;
1698
std::string const TLS_VERSION_DEFAULT = "1.2";
1699
1700
// Stuff for curl download/upload
1701
using cmFileCommandVectorOfChar = std::vector<char>;
1702
1703
std::string CurlCompatFileURL(std::string url)
1704
0
{
1705
0
  if (cmHasLiteralPrefix(url, "file://")) {
1706
    // libcurl 7.77 and below accidentally allowed spaces in file:// URLs,
1707
    // which CMake has long accepted as a result.  Explicitly encode spaces.
1708
0
    cmSystemTools::ReplaceString(url, " ", "%20");
1709
0
  }
1710
0
  return url;
1711
0
}
1712
1713
size_t cmWriteToFileCallback(void* ptr, size_t size, size_t nmemb, void* data)
1714
0
{
1715
0
  int realsize = static_cast<int>(size * nmemb);
1716
0
  cmsys::ofstream* fout = static_cast<cmsys::ofstream*>(data);
1717
0
  if (fout) {
1718
0
    char const* chPtr = static_cast<char*>(ptr);
1719
0
    if (!fout->write(chPtr, realsize)) {
1720
0
      return CURL_WRITEFUNC_ERROR;
1721
0
    }
1722
0
  }
1723
0
  return realsize;
1724
0
}
1725
1726
size_t cmWriteToMemoryCallback(void* ptr, size_t size, size_t nmemb,
1727
                               void* data)
1728
0
{
1729
0
  int realsize = static_cast<int>(size * nmemb);
1730
0
  char const* chPtr = static_cast<char*>(ptr);
1731
0
  cm::append(*static_cast<cmFileCommandVectorOfChar*>(data), chPtr,
1732
0
             chPtr + realsize);
1733
0
  return realsize;
1734
0
}
1735
1736
int cmFileCommandCurlDebugCallback(CURL*, curl_infotype type, char* chPtr,
1737
                                   size_t size, void* data)
1738
0
{
1739
0
  cmFileCommandVectorOfChar& vec =
1740
0
    *static_cast<cmFileCommandVectorOfChar*>(data);
1741
0
  switch (type) {
1742
0
    case CURLINFO_TEXT:
1743
0
    case CURLINFO_HEADER_IN:
1744
0
    case CURLINFO_HEADER_OUT:
1745
0
      cm::append(vec, chPtr, chPtr + size);
1746
0
      break;
1747
0
    case CURLINFO_DATA_IN:
1748
0
    case CURLINFO_DATA_OUT:
1749
0
    case CURLINFO_SSL_DATA_IN:
1750
0
    case CURLINFO_SSL_DATA_OUT: {
1751
0
      char buf[128];
1752
0
      int n =
1753
0
        snprintf(buf, sizeof(buf), "[%" KWIML_INT_PRIu64 " bytes data]\n",
1754
0
                 static_cast<KWIML_INT_uint64_t>(size));
1755
0
      if (n > 0) {
1756
0
        cm::append(vec, buf, buf + n);
1757
0
      }
1758
0
    } break;
1759
0
    default:
1760
0
      break;
1761
0
  }
1762
0
  return 0;
1763
0
}
1764
1765
#  if defined(LIBCURL_VERSION_NUM) && LIBCURL_VERSION_NUM >= 0x072000
1766
CURLoption const CM_CURLOPT_XFERINFOFUNCTION = CURLOPT_XFERINFOFUNCTION;
1767
using cm_curl_off_t = curl_off_t;
1768
#  else
1769
CURLoption const CM_CURLOPT_XFERINFOFUNCTION = CURLOPT_PROGRESSFUNCTION;
1770
using cm_curl_off_t = double;
1771
#  endif
1772
1773
class cURLProgressHelper
1774
{
1775
public:
1776
  cURLProgressHelper(cmMakefile* mf, char const* text)
1777
0
    : Makefile(mf)
1778
0
    , Text(text)
1779
0
  {
1780
0
  }
1781
1782
  bool UpdatePercentage(cm_curl_off_t value, cm_curl_off_t total,
1783
                        std::string& status)
1784
0
  {
1785
0
    long OldPercentage = this->CurrentPercentage;
1786
1787
0
    if (total > 0) {
1788
0
      this->CurrentPercentage = std::lround(
1789
0
        static_cast<double>(value) / static_cast<double>(total) * 100.0);
1790
0
      if (this->CurrentPercentage > 100) {
1791
        // Avoid extra progress reports for unexpected data beyond total.
1792
0
        this->CurrentPercentage = 100;
1793
0
      }
1794
0
    }
1795
1796
0
    bool updated = (OldPercentage != this->CurrentPercentage);
1797
1798
0
    if (updated) {
1799
0
      status =
1800
0
        cmStrCat('[', this->Text, ' ', this->CurrentPercentage, "% complete]");
1801
0
    }
1802
1803
0
    return updated;
1804
0
  }
1805
1806
0
  cmMakefile* GetMakefile() { return this->Makefile; }
1807
1808
private:
1809
  long CurrentPercentage = -1;
1810
  cmMakefile* Makefile;
1811
  std::string Text;
1812
};
1813
1814
int cmFileDownloadProgressCallback(void* clientp, cm_curl_off_t dltotal,
1815
                                   cm_curl_off_t dlnow, cm_curl_off_t ultotal,
1816
                                   cm_curl_off_t ulnow)
1817
0
{
1818
0
  cURLProgressHelper* helper = reinterpret_cast<cURLProgressHelper*>(clientp);
1819
1820
0
  static_cast<void>(ultotal);
1821
0
  static_cast<void>(ulnow);
1822
1823
0
  std::string status;
1824
0
  if (helper->UpdatePercentage(dlnow, dltotal, status)) {
1825
0
    cmMakefile* mf = helper->GetMakefile();
1826
0
    mf->DisplayStatus(status, -1);
1827
0
  }
1828
1829
0
  return 0;
1830
0
}
1831
1832
int cmFileUploadProgressCallback(void* clientp, cm_curl_off_t dltotal,
1833
                                 cm_curl_off_t dlnow, cm_curl_off_t ultotal,
1834
                                 cm_curl_off_t ulnow)
1835
0
{
1836
0
  cURLProgressHelper* helper = reinterpret_cast<cURLProgressHelper*>(clientp);
1837
1838
0
  static_cast<void>(dltotal);
1839
0
  static_cast<void>(dlnow);
1840
1841
0
  std::string status;
1842
0
  if (helper->UpdatePercentage(ulnow, ultotal, status)) {
1843
0
    cmMakefile* mf = helper->GetMakefile();
1844
0
    mf->DisplayStatus(status, -1);
1845
0
  }
1846
1847
0
  return 0;
1848
0
}
1849
1850
class cURLEasyGuard
1851
{
1852
public:
1853
  cURLEasyGuard(CURL* easy)
1854
0
    : Easy(easy)
1855
0
  {
1856
0
  }
1857
1858
  ~cURLEasyGuard()
1859
0
  {
1860
0
    if (this->Easy) {
1861
0
      ::curl_easy_cleanup(this->Easy);
1862
0
    }
1863
0
  }
1864
1865
  cURLEasyGuard(cURLEasyGuard const&) = delete;
1866
  cURLEasyGuard& operator=(cURLEasyGuard const&) = delete;
1867
1868
0
  void release() { this->Easy = nullptr; }
1869
1870
private:
1871
  ::CURL* Easy;
1872
};
1873
1874
#endif
1875
1876
#define check_curl_result(result, errstr)                                     \
1877
0
  do {                                                                        \
1878
0
    if (result != CURLE_OK) {                                                 \
1879
0
      std::string e(errstr);                                                  \
1880
0
      e += ::curl_easy_strerror(result);                                      \
1881
0
      status.SetError(e);                                                     \
1882
0
      return false;                                                           \
1883
0
    }                                                                         \
1884
0
  } while (false)
1885
1886
bool HandleDownloadCommand(std::vector<std::string> const& args,
1887
                           cmExecutionStatus& status)
1888
0
{
1889
0
#if !defined(CMAKE_BOOTSTRAP)
1890
0
  auto i = args.begin();
1891
0
  if (args.size() < 2) {
1892
0
    status.SetError("DOWNLOAD must be called with at least two arguments.");
1893
0
    return false;
1894
0
  }
1895
0
  ++i; // Get rid of subcommand
1896
0
  std::string url = *i;
1897
0
  ++i;
1898
0
  std::string file;
1899
1900
0
  long timeout = 0;
1901
0
  long inactivity_timeout = 0;
1902
0
  std::string logVar;
1903
0
  std::string statusVar;
1904
0
  cm::optional<std::string> tlsVersionOpt;
1905
0
  cm::optional<bool> tlsVerifyOpt;
1906
0
  cmValue cainfo = status.GetMakefile().GetDefinition("CMAKE_TLS_CAINFO");
1907
0
  std::string netrc_level =
1908
0
    status.GetMakefile().GetSafeDefinition("CMAKE_NETRC");
1909
0
  std::string netrc_file =
1910
0
    status.GetMakefile().GetSafeDefinition("CMAKE_NETRC_FILE");
1911
0
  std::string expectedHash;
1912
0
  std::string hashMatchMSG;
1913
0
  std::unique_ptr<cmCryptoHash> hash;
1914
0
  bool showProgress = false;
1915
0
  std::string userpwd;
1916
1917
0
  std::vector<std::string> curl_headers;
1918
0
  std::vector<std::pair<std::string, cm::optional<std::string>>> curl_ranges;
1919
1920
0
  while (i != args.end()) {
1921
0
    if (*i == "TIMEOUT") {
1922
0
      ++i;
1923
0
      if (i != args.end()) {
1924
0
        timeout = atol(i->c_str());
1925
0
      } else {
1926
0
        status.SetError("DOWNLOAD missing time for TIMEOUT.");
1927
0
        return false;
1928
0
      }
1929
0
    } else if (*i == "INACTIVITY_TIMEOUT") {
1930
0
      ++i;
1931
0
      if (i != args.end()) {
1932
0
        inactivity_timeout = atol(i->c_str());
1933
0
      } else {
1934
0
        status.SetError("DOWNLOAD missing time for INACTIVITY_TIMEOUT.");
1935
0
        return false;
1936
0
      }
1937
0
    } else if (*i == "LOG") {
1938
0
      ++i;
1939
0
      if (i == args.end()) {
1940
0
        status.SetError("DOWNLOAD missing VAR for LOG.");
1941
0
        return false;
1942
0
      }
1943
0
      logVar = *i;
1944
0
    } else if (*i == "STATUS") {
1945
0
      ++i;
1946
0
      if (i == args.end()) {
1947
0
        status.SetError("DOWNLOAD missing VAR for STATUS.");
1948
0
        return false;
1949
0
      }
1950
0
      statusVar = *i;
1951
0
    } else if (*i == "TLS_VERSION") {
1952
0
      ++i;
1953
0
      if (i != args.end()) {
1954
0
        tlsVersionOpt = *i;
1955
0
      } else {
1956
0
        status.SetError("DOWNLOAD missing value for TLS_VERSION.");
1957
0
        return false;
1958
0
      }
1959
0
    } else if (*i == "TLS_VERIFY") {
1960
0
      ++i;
1961
0
      if (i != args.end()) {
1962
0
        tlsVerifyOpt = cmIsOn(*i);
1963
0
      } else {
1964
0
        status.SetError("DOWNLOAD missing bool value for TLS_VERIFY.");
1965
0
        return false;
1966
0
      }
1967
0
    } else if (*i == "TLS_CAINFO") {
1968
0
      ++i;
1969
0
      if (i != args.end()) {
1970
0
        cainfo = cmValue(*i);
1971
0
      } else {
1972
0
        status.SetError("DOWNLOAD missing file value for TLS_CAINFO.");
1973
0
        return false;
1974
0
      }
1975
0
    } else if (*i == "NETRC_FILE") {
1976
0
      ++i;
1977
0
      if (i != args.end()) {
1978
0
        netrc_file = *i;
1979
0
      } else {
1980
0
        status.SetError("DOWNLOAD missing file value for NETRC_FILE.");
1981
0
        return false;
1982
0
      }
1983
0
    } else if (*i == "NETRC") {
1984
0
      ++i;
1985
0
      if (i != args.end()) {
1986
0
        netrc_level = *i;
1987
0
      } else {
1988
0
        status.SetError("DOWNLOAD missing level value for NETRC.");
1989
0
        return false;
1990
0
      }
1991
0
    } else if (*i == "EXPECTED_MD5") {
1992
0
      ++i;
1993
0
      if (i == args.end()) {
1994
0
        status.SetError("DOWNLOAD missing sum value for EXPECTED_MD5.");
1995
0
        return false;
1996
0
      }
1997
0
      hash = cm::make_unique<cmCryptoHash>(cmCryptoHash::AlgoMD5);
1998
0
      hashMatchMSG = "MD5 sum";
1999
0
      expectedHash = cmSystemTools::LowerCase(*i);
2000
0
    } else if (*i == "SHOW_PROGRESS") {
2001
0
      showProgress = true;
2002
0
    } else if (*i == "EXPECTED_HASH") {
2003
0
      ++i;
2004
0
      if (i == args.end()) {
2005
0
        status.SetError("DOWNLOAD missing ALGO=value for EXPECTED_HASH.");
2006
0
        return false;
2007
0
      }
2008
0
      std::string::size_type pos = i->find("=");
2009
0
      if (pos == std::string::npos) {
2010
0
        std::string err =
2011
0
          cmStrCat("DOWNLOAD EXPECTED_HASH expects ALGO=value but got: ", *i);
2012
0
        status.SetError(err);
2013
0
        return false;
2014
0
      }
2015
0
      std::string algo = i->substr(0, pos);
2016
0
      expectedHash = cmSystemTools::LowerCase(i->substr(pos + 1));
2017
0
      hash = cmCryptoHash::New(algo);
2018
0
      if (!hash) {
2019
0
        std::string err =
2020
0
          cmStrCat("DOWNLOAD EXPECTED_HASH given unknown ALGO: ", algo);
2021
0
        status.SetError(err);
2022
0
        return false;
2023
0
      }
2024
0
      hashMatchMSG = algo + " hash";
2025
0
    } else if (*i == "USERPWD") {
2026
0
      ++i;
2027
0
      if (i == args.end()) {
2028
0
        status.SetError("DOWNLOAD missing string for USERPWD.");
2029
0
        return false;
2030
0
      }
2031
0
      userpwd = *i;
2032
0
    } else if (*i == "HTTPHEADER") {
2033
0
      ++i;
2034
0
      if (i == args.end()) {
2035
0
        status.SetError("DOWNLOAD missing string for HTTPHEADER.");
2036
0
        return false;
2037
0
      }
2038
0
      curl_headers.push_back(*i);
2039
0
    } else if (*i == "RANGE_START") {
2040
0
      ++i;
2041
0
      if (i == args.end()) {
2042
0
        status.SetError("DOWNLOAD missing value for RANGE_START.");
2043
0
        return false;
2044
0
      }
2045
0
      curl_ranges.emplace_back(*i, cm::nullopt);
2046
0
    } else if (*i == "RANGE_END") {
2047
0
      ++i;
2048
0
      if (curl_ranges.empty()) {
2049
0
        curl_ranges.emplace_back("0", *i);
2050
0
      } else {
2051
0
        auto& last_range = curl_ranges.back();
2052
0
        if (!last_range.second.has_value()) {
2053
0
          last_range.second = *i;
2054
0
        } else {
2055
0
          status.SetError("Multiple RANGE_END values is provided without "
2056
0
                          "the corresponding RANGE_START.");
2057
0
          return false;
2058
0
        }
2059
0
      }
2060
0
    } else if (file.empty()) {
2061
0
      file = *i;
2062
0
    } else {
2063
      // Do not return error for compatibility reason.
2064
0
      status.GetMakefile().IssueDiagnostic(
2065
0
        cmDiagnostics::CMD_AUTHOR, cmStrCat("Unexpected argument: ", *i));
2066
0
    }
2067
0
    ++i;
2068
0
  }
2069
2070
0
  if (!tlsVerifyOpt.has_value()) {
2071
0
    if (cmValue v = status.GetMakefile().GetDefinition("CMAKE_TLS_VERIFY")) {
2072
0
      tlsVerifyOpt = v.IsOn();
2073
0
    }
2074
0
  }
2075
0
  if (!tlsVerifyOpt.has_value()) {
2076
0
    if (cm::optional<std::string> v =
2077
0
          cmSystemTools::GetEnvVar("CMAKE_TLS_VERIFY")) {
2078
0
      tlsVerifyOpt = cmIsOn(*v);
2079
0
    }
2080
0
  }
2081
0
  bool tlsVerifyDefaulted = false;
2082
0
  if (!tlsVerifyOpt.has_value()) {
2083
0
    tlsVerifyOpt = TLS_VERIFY_DEFAULT;
2084
0
    tlsVerifyDefaulted = true;
2085
0
  }
2086
2087
0
  if (!tlsVersionOpt.has_value()) {
2088
0
    if (cmValue v = status.GetMakefile().GetDefinition("CMAKE_TLS_VERSION")) {
2089
0
      tlsVersionOpt = *v;
2090
0
    }
2091
0
  }
2092
0
  if (!tlsVersionOpt.has_value()) {
2093
0
    if (cm::optional<std::string> v =
2094
0
          cmSystemTools::GetEnvVar("CMAKE_TLS_VERSION")) {
2095
0
      tlsVersionOpt = std::move(v);
2096
0
    }
2097
0
  }
2098
0
  bool tlsVersionDefaulted = false;
2099
0
  if (!tlsVersionOpt.has_value()) {
2100
0
    tlsVersionOpt = TLS_VERSION_DEFAULT;
2101
0
    tlsVersionDefaulted = true;
2102
0
  }
2103
2104
  // Can't calculate hash if we don't save the file.
2105
  // TODO Incrementally calculate hash in the write callback as the file is
2106
  // being downloaded so this check can be relaxed.
2107
0
  if (file.empty() && hash) {
2108
0
    status.SetError("DOWNLOAD cannot calculate hash if file is not saved.");
2109
0
    return false;
2110
0
  }
2111
  // If file exists already, and caller specified an expected md5 or sha,
2112
  // and the existing file already has the expected hash, then simply
2113
  // return.
2114
  //
2115
0
  if (!file.empty() && cmSystemTools::FileExists(file) && hash.get()) {
2116
0
    std::string msg;
2117
0
    std::string actualHash = hash->HashFile(file);
2118
0
    if (actualHash == expectedHash) {
2119
0
      msg = cmStrCat("skipping download as file already exists with expected ",
2120
0
                     hashMatchMSG, '"');
2121
0
      if (!statusVar.empty()) {
2122
0
        status.GetMakefile().AddDefinition(statusVar, cmStrCat(0, ";\"", msg));
2123
0
      }
2124
0
      return true;
2125
0
    }
2126
0
  }
2127
  // Make sure parent directory exists so we can write to the file
2128
  // as we receive downloaded bits from curl...
2129
  //
2130
0
  if (!file.empty()) {
2131
0
    std::string dir = cmSystemTools::GetFilenamePath(file);
2132
0
    if (!dir.empty() && !cmSystemTools::FileExists(dir) &&
2133
0
        !cmSystemTools::MakeDirectory(dir)) {
2134
0
      std::string errstring = "DOWNLOAD error: cannot create directory '" +
2135
0
        dir +
2136
0
        "' - Specify file by full path name and verify that you "
2137
0
        "have directory creation and file write privileges.";
2138
0
      status.SetError(errstring);
2139
0
      return false;
2140
0
    }
2141
0
  }
2142
2143
0
  cmsys::ofstream fout;
2144
0
  if (!file.empty()) {
2145
0
    fout.open(file.c_str(), std::ios::binary);
2146
0
    if (!fout) {
2147
0
      status.SetError(cmStrCat("DOWNLOAD cannot open file for write\n"
2148
0
                               "  file: \"",
2149
0
                               file, '"'));
2150
0
      return false;
2151
0
    }
2152
0
  }
2153
2154
0
  url = CurlCompatFileURL(url);
2155
2156
0
  ::CURL* curl;
2157
0
  cm_curl_global_init(CURL_GLOBAL_DEFAULT);
2158
0
  curl = cm_curl_easy_init();
2159
0
  if (!curl) {
2160
0
    status.SetError("DOWNLOAD error initializing curl.");
2161
0
    return false;
2162
0
  }
2163
2164
0
  cURLEasyGuard g_curl(curl);
2165
0
  ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
2166
0
  check_curl_result(res, "DOWNLOAD cannot set url: ");
2167
2168
  // enable HTTP ERROR parsing
2169
0
  res = ::curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
2170
0
  check_curl_result(res, "DOWNLOAD cannot set http failure option: ");
2171
2172
0
  curl_version_info_data* cv = curl_version_info(CURLVERSION_FIRST);
2173
0
  res = ::curl_easy_setopt(
2174
0
    curl, CURLOPT_USERAGENT,
2175
0
    cmStrCat("curl/", cv ? cv->version : LIBCURL_VERSION).c_str());
2176
0
  check_curl_result(res, "DOWNLOAD cannot set user agent option: ");
2177
2178
0
  res = ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cmWriteToFileCallback);
2179
0
  check_curl_result(res, "DOWNLOAD cannot set write function: ");
2180
2181
0
  res = ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
2182
0
                           cmFileCommandCurlDebugCallback);
2183
0
  check_curl_result(res, "DOWNLOAD cannot set debug function: ");
2184
2185
0
  if (tlsVersionOpt.has_value()) {
2186
0
    if (cm::optional<int> v = cmCurlParseTLSVersion(*tlsVersionOpt)) {
2187
0
      res = ::curl_easy_setopt(curl, CURLOPT_SSLVERSION, *v);
2188
0
      if (tlsVersionDefaulted && res == CURLE_NOT_BUILT_IN) {
2189
0
        res = CURLE_OK;
2190
0
      }
2191
0
      check_curl_result(res,
2192
0
                        cmStrCat("DOWNLOAD cannot set TLS/SSL version ",
2193
0
                                 *tlsVersionOpt, ": "));
2194
0
    } else {
2195
0
      status.SetError(
2196
0
        cmStrCat("DOWNLOAD given unknown TLS/SSL version ", *tlsVersionOpt));
2197
0
      return false;
2198
0
    }
2199
0
  }
2200
2201
  // check to see if TLS verification is requested
2202
0
  if (tlsVerifyOpt.has_value() && tlsVerifyOpt.value()) {
2203
0
    res = ::curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
2204
0
    check_curl_result(res, "DOWNLOAD cannot set TLS/SSL Verify on: ");
2205
0
  } else {
2206
0
    res = ::curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
2207
0
    check_curl_result(res, "DOWNLOAD cannot set TLS/SSL Verify off: ");
2208
0
  }
2209
2210
0
  for (auto const& range : curl_ranges) {
2211
0
    std::string curl_range =
2212
0
      cmStrCat(range.first, '-',
2213
0
               (range.second.has_value() ? range.second.value() : ""));
2214
0
    res = ::curl_easy_setopt(curl, CURLOPT_RANGE, curl_range.c_str());
2215
0
    check_curl_result(res, "DOWNLOAD cannot set range: ");
2216
0
  }
2217
2218
  // check to see if a CAINFO file has been specified
2219
  // command arg comes first
2220
0
  std::string const& cainfo_err = cmCurlSetCAInfo(curl, cainfo);
2221
0
  if (!cainfo_err.empty()) {
2222
0
    status.SetError(cainfo_err);
2223
0
    return false;
2224
0
  }
2225
2226
  // check to see if netrc parameters have been specified
2227
  // local command args takes precedence over CMAKE_NETRC*
2228
0
  netrc_level = cmSystemTools::UpperCase(netrc_level);
2229
0
  std::string const& netrc_option_err =
2230
0
    cmCurlSetNETRCOption(curl, netrc_level, netrc_file);
2231
0
  if (!netrc_option_err.empty()) {
2232
0
    status.SetError(netrc_option_err);
2233
0
    return false;
2234
0
  }
2235
2236
0
  cmFileCommandVectorOfChar chunkDebug;
2237
2238
0
  res = ::curl_easy_setopt(curl, CURLOPT_WRITEDATA,
2239
0
                           file.empty() ? nullptr : &fout);
2240
0
  check_curl_result(res, "DOWNLOAD cannot set write data: ");
2241
2242
0
  res = ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &chunkDebug);
2243
0
  check_curl_result(res, "DOWNLOAD cannot set debug data: ");
2244
2245
0
  res = ::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
2246
0
  check_curl_result(res, "DOWNLOAD cannot set follow-redirect option: ");
2247
2248
0
  if (!logVar.empty()) {
2249
0
    res = ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
2250
0
    check_curl_result(res, "DOWNLOAD cannot set verbose: ");
2251
0
  }
2252
2253
0
  if (timeout > 0) {
2254
0
    res = ::curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
2255
0
    check_curl_result(res, "DOWNLOAD cannot set timeout: ");
2256
0
  }
2257
2258
0
  if (inactivity_timeout > 0) {
2259
    // Give up if there is no progress for a long time.
2260
0
    ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1);
2261
0
    ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, inactivity_timeout);
2262
0
  }
2263
2264
  // Need the progress helper's scope to last through the duration of
2265
  // the curl_easy_perform call... so this object is declared at function
2266
  // scope intentionally, rather than inside the "if(showProgress)"
2267
  // block...
2268
  //
2269
0
  cURLProgressHelper helper(&status.GetMakefile(), "download");
2270
2271
0
  if (showProgress) {
2272
0
    res = ::curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
2273
0
    check_curl_result(res, "DOWNLOAD cannot set noprogress value: ");
2274
2275
0
    res = ::curl_easy_setopt(curl, CM_CURLOPT_XFERINFOFUNCTION,
2276
0
                             cmFileDownloadProgressCallback);
2277
0
    check_curl_result(res, "DOWNLOAD cannot set progress function: ");
2278
2279
0
    res = ::curl_easy_setopt(curl, CURLOPT_PROGRESSDATA,
2280
0
                             reinterpret_cast<void*>(&helper));
2281
0
    check_curl_result(res, "DOWNLOAD cannot set progress data: ");
2282
0
  }
2283
2284
0
  if (!userpwd.empty()) {
2285
0
    res = ::curl_easy_setopt(curl, CURLOPT_USERPWD, userpwd.c_str());
2286
0
    check_curl_result(res, "DOWNLOAD cannot set user password: ");
2287
0
  }
2288
2289
0
  struct curl_slist* headers = nullptr;
2290
0
  for (std::string const& h : curl_headers) {
2291
0
    headers = ::curl_slist_append(headers, h.c_str());
2292
0
  }
2293
0
  ::curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
2294
2295
0
  res = ::curl_easy_perform(curl);
2296
2297
0
  ::curl_slist_free_all(headers);
2298
2299
  /* always cleanup */
2300
0
  g_curl.release();
2301
0
  ::curl_easy_cleanup(curl);
2302
2303
  // Explicitly close the file so we can check for write errors.
2304
0
  if (!file.empty()) {
2305
0
    fout.close();
2306
0
    if (!fout) {
2307
0
      res = CURLE_WRITE_ERROR;
2308
0
    }
2309
0
  }
2310
2311
0
  if (!statusVar.empty()) {
2312
0
    std::string m = curl_easy_strerror(res);
2313
0
    if ((res == CURLE_SSL_CONNECT_ERROR ||
2314
0
         res == CURLE_PEER_FAILED_VERIFICATION) &&
2315
0
        tlsVerifyDefaulted) {
2316
0
      m = cmStrCat(
2317
0
        std::move(m),
2318
0
        ".  If this is due to https certificate verification failure, one may "
2319
0
        "set environment variable CMAKE_TLS_VERIFY=0 to suppress it.");
2320
0
    }
2321
0
    status.GetMakefile().AddDefinition(
2322
0
      statusVar, cmStrCat(static_cast<int>(res), ";\"", std::move(m), '"'));
2323
0
  }
2324
2325
0
  ::curl_global_cleanup();
2326
2327
  // Ensure requested curl logs are returned (especially in case of failure)
2328
  //
2329
0
  if (!logVar.empty()) {
2330
0
    chunkDebug.push_back(0);
2331
0
    status.GetMakefile().AddDefinition(logVar, chunkDebug.data());
2332
0
  }
2333
2334
  // Verify MD5 sum if requested:
2335
  //
2336
0
  if (hash) {
2337
0
    if (res != CURLE_OK) {
2338
0
      status.SetError(
2339
0
        cmStrCat("DOWNLOAD cannot compute hash on failed download\n"
2340
0
                 "  from url: \"",
2341
0
                 url, "\"\n  status: [", static_cast<int>(res), ";\"",
2342
0
                 ::curl_easy_strerror(res), "\"]"));
2343
0
      return false;
2344
0
    }
2345
2346
0
    std::string actualHash = hash->HashFile(file);
2347
0
    if (actualHash.empty()) {
2348
0
      status.SetError(cmStrCat("DOWNLOAD cannot compute hash on download\n"
2349
0
                               "  for file: \"",
2350
0
                               file, '"'));
2351
0
      return false;
2352
0
    }
2353
2354
0
    if (expectedHash != actualHash) {
2355
0
      if (!statusVar.empty() && res == 0) {
2356
0
        status.GetMakefile().AddDefinition(statusVar,
2357
0
                                           "1;HASH mismatch: "
2358
0
                                           "expected: " +
2359
0
                                             expectedHash +
2360
0
                                             " actual: " + actualHash);
2361
0
      }
2362
2363
0
      status.SetError(cmStrCat("DOWNLOAD HASH mismatch\n"
2364
0
                               "  for file: \"",
2365
0
                               file,
2366
0
                               "\"\n"
2367
0
                               "    expected hash: \"",
2368
0
                               expectedHash,
2369
0
                               "\"\n"
2370
0
                               "      actual hash: \"",
2371
0
                               actualHash, "\"\n"));
2372
0
      return false;
2373
0
    }
2374
0
  }
2375
2376
0
  return true;
2377
#else
2378
  status.SetError("DOWNLOAD not supported by bootstrap cmake.");
2379
  return false;
2380
#endif
2381
0
}
2382
2383
bool HandleUploadCommand(std::vector<std::string> const& args,
2384
                         cmExecutionStatus& status)
2385
0
{
2386
0
#if !defined(CMAKE_BOOTSTRAP)
2387
0
  if (args.size() < 3) {
2388
0
    status.SetError("UPLOAD must be called with at least three arguments.");
2389
0
    return false;
2390
0
  }
2391
0
  auto i = args.begin();
2392
0
  ++i;
2393
0
  std::string filename = *i;
2394
0
  ++i;
2395
0
  std::string url = *i;
2396
0
  ++i;
2397
2398
0
  long timeout = 0;
2399
0
  long inactivity_timeout = 0;
2400
0
  std::string logVar;
2401
0
  std::string statusVar;
2402
0
  bool showProgress = false;
2403
0
  cm::optional<std::string> tlsVersionOpt;
2404
0
  cm::optional<bool> tlsVerifyOpt;
2405
0
  cmValue cainfo = status.GetMakefile().GetDefinition("CMAKE_TLS_CAINFO");
2406
0
  std::string userpwd;
2407
0
  std::string netrc_level =
2408
0
    status.GetMakefile().GetSafeDefinition("CMAKE_NETRC");
2409
0
  std::string netrc_file =
2410
0
    status.GetMakefile().GetSafeDefinition("CMAKE_NETRC_FILE");
2411
2412
0
  std::vector<std::string> curl_headers;
2413
2414
0
  while (i != args.end()) {
2415
0
    if (*i == "TIMEOUT") {
2416
0
      ++i;
2417
0
      if (i != args.end()) {
2418
0
        timeout = atol(i->c_str());
2419
0
      } else {
2420
0
        status.SetError("UPLOAD missing time for TIMEOUT.");
2421
0
        return false;
2422
0
      }
2423
0
    } else if (*i == "INACTIVITY_TIMEOUT") {
2424
0
      ++i;
2425
0
      if (i != args.end()) {
2426
0
        inactivity_timeout = atol(i->c_str());
2427
0
      } else {
2428
0
        status.SetError("UPLOAD missing time for INACTIVITY_TIMEOUT.");
2429
0
        return false;
2430
0
      }
2431
0
    } else if (*i == "LOG") {
2432
0
      ++i;
2433
0
      if (i == args.end()) {
2434
0
        status.SetError("UPLOAD missing VAR for LOG.");
2435
0
        return false;
2436
0
      }
2437
0
      logVar = *i;
2438
0
    } else if (*i == "STATUS") {
2439
0
      ++i;
2440
0
      if (i == args.end()) {
2441
0
        status.SetError("UPLOAD missing VAR for STATUS.");
2442
0
        return false;
2443
0
      }
2444
0
      statusVar = *i;
2445
0
    } else if (*i == "SHOW_PROGRESS") {
2446
0
      showProgress = true;
2447
0
    } else if (*i == "TLS_VERSION") {
2448
0
      ++i;
2449
0
      if (i != args.end()) {
2450
0
        tlsVersionOpt = *i;
2451
0
      } else {
2452
0
        status.SetError("UPLOAD missing value for TLS_VERSION.");
2453
0
        return false;
2454
0
      }
2455
0
    } else if (*i == "TLS_VERIFY") {
2456
0
      ++i;
2457
0
      if (i != args.end()) {
2458
0
        tlsVerifyOpt = cmIsOn(*i);
2459
0
      } else {
2460
0
        status.SetError("UPLOAD missing bool value for TLS_VERIFY.");
2461
0
        return false;
2462
0
      }
2463
0
    } else if (*i == "TLS_CAINFO") {
2464
0
      ++i;
2465
0
      if (i != args.end()) {
2466
0
        cainfo = cmValue(*i);
2467
0
      } else {
2468
0
        status.SetError("UPLOAD missing file value for TLS_CAINFO.");
2469
0
        return false;
2470
0
      }
2471
0
    } else if (*i == "NETRC_FILE") {
2472
0
      ++i;
2473
0
      if (i != args.end()) {
2474
0
        netrc_file = *i;
2475
0
      } else {
2476
0
        status.SetError("UPLOAD missing file value for NETRC_FILE.");
2477
0
        return false;
2478
0
      }
2479
0
    } else if (*i == "NETRC") {
2480
0
      ++i;
2481
0
      if (i != args.end()) {
2482
0
        netrc_level = *i;
2483
0
      } else {
2484
0
        status.SetError("UPLOAD missing level value for NETRC.");
2485
0
        return false;
2486
0
      }
2487
0
    } else if (*i == "USERPWD") {
2488
0
      ++i;
2489
0
      if (i == args.end()) {
2490
0
        status.SetError("UPLOAD missing string for USERPWD.");
2491
0
        return false;
2492
0
      }
2493
0
      userpwd = *i;
2494
0
    } else if (*i == "HTTPHEADER") {
2495
0
      ++i;
2496
0
      if (i == args.end()) {
2497
0
        status.SetError("UPLOAD missing string for HTTPHEADER.");
2498
0
        return false;
2499
0
      }
2500
0
      curl_headers.push_back(*i);
2501
0
    } else {
2502
      // Do not return error for compatibility reason.
2503
0
      status.GetMakefile().IssueDiagnostic(
2504
0
        cmDiagnostics::CMD_AUTHOR, cmStrCat("Unexpected argument: ", *i));
2505
0
    }
2506
2507
0
    ++i;
2508
0
  }
2509
2510
0
  if (!tlsVerifyOpt.has_value()) {
2511
0
    if (cmValue v = status.GetMakefile().GetDefinition("CMAKE_TLS_VERIFY")) {
2512
0
      tlsVerifyOpt = v.IsOn();
2513
0
    }
2514
0
  }
2515
0
  if (!tlsVerifyOpt.has_value()) {
2516
0
    if (cm::optional<std::string> v =
2517
0
          cmSystemTools::GetEnvVar("CMAKE_TLS_VERIFY")) {
2518
0
      tlsVerifyOpt = cmIsOn(*v);
2519
0
    }
2520
0
  }
2521
0
  bool tlsVerifyDefaulted = false;
2522
0
  if (!tlsVerifyOpt.has_value()) {
2523
0
    tlsVerifyOpt = TLS_VERIFY_DEFAULT;
2524
0
    tlsVerifyDefaulted = true;
2525
0
  }
2526
2527
0
  if (!tlsVersionOpt.has_value()) {
2528
0
    if (cmValue v = status.GetMakefile().GetDefinition("CMAKE_TLS_VERSION")) {
2529
0
      tlsVersionOpt = *v;
2530
0
    }
2531
0
  }
2532
0
  if (!tlsVersionOpt.has_value()) {
2533
0
    if (cm::optional<std::string> v =
2534
0
          cmSystemTools::GetEnvVar("CMAKE_TLS_VERSION")) {
2535
0
      tlsVersionOpt = std::move(v);
2536
0
    }
2537
0
  }
2538
0
  bool tlsVersionDefaulted = false;
2539
0
  if (!tlsVersionOpt.has_value()) {
2540
0
    tlsVersionOpt = TLS_VERSION_DEFAULT;
2541
0
    tlsVersionDefaulted = true;
2542
0
  }
2543
2544
  // Open file for reading:
2545
  //
2546
0
  FILE* fin = cmsys::SystemTools::Fopen(filename, "rb");
2547
0
  if (!fin) {
2548
0
    std::string errStr =
2549
0
      cmStrCat("UPLOAD cannot open file '", filename, "' for reading.");
2550
0
    status.SetError(errStr);
2551
0
    return false;
2552
0
  }
2553
2554
0
  unsigned long file_size = cmsys::SystemTools::FileLength(filename);
2555
2556
0
  url = CurlCompatFileURL(url);
2557
2558
0
  ::CURL* curl;
2559
0
  cm_curl_global_init(CURL_GLOBAL_DEFAULT);
2560
0
  curl = cm_curl_easy_init();
2561
0
  if (!curl) {
2562
0
    status.SetError("UPLOAD error initializing curl.");
2563
0
    fclose(fin);
2564
0
    return false;
2565
0
  }
2566
2567
0
  cURLEasyGuard g_curl(curl);
2568
2569
  // enable HTTP ERROR parsing
2570
0
  ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
2571
0
  check_curl_result(res, "UPLOAD cannot set fail on error flag: ");
2572
2573
  // enable uploading
2574
0
  res = ::curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
2575
0
  check_curl_result(res, "UPLOAD cannot set upload flag: ");
2576
2577
0
  res = ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
2578
0
  check_curl_result(res, "UPLOAD cannot set url: ");
2579
2580
0
  res =
2581
0
    ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cmWriteToMemoryCallback);
2582
0
  check_curl_result(res, "UPLOAD cannot set write function: ");
2583
2584
0
  res = ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
2585
0
                           cmFileCommandCurlDebugCallback);
2586
0
  check_curl_result(res, "UPLOAD cannot set debug function: ");
2587
2588
0
  if (tlsVersionOpt.has_value()) {
2589
0
    if (cm::optional<int> v = cmCurlParseTLSVersion(*tlsVersionOpt)) {
2590
0
      res = ::curl_easy_setopt(curl, CURLOPT_SSLVERSION, *v);
2591
0
      if (tlsVersionDefaulted && res == CURLE_NOT_BUILT_IN) {
2592
0
        res = CURLE_OK;
2593
0
      }
2594
0
      check_curl_result(
2595
0
        res,
2596
0
        cmStrCat("UPLOAD cannot set TLS/SSL version ", *tlsVersionOpt, ": "));
2597
0
    } else {
2598
0
      status.SetError(
2599
0
        cmStrCat("UPLOAD given unknown TLS/SSL version ", *tlsVersionOpt));
2600
0
      return false;
2601
0
    }
2602
0
  }
2603
2604
  // check to see if TLS verification is requested
2605
0
  if (tlsVerifyOpt.has_value() && tlsVerifyOpt.value()) {
2606
0
    res = ::curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
2607
0
    check_curl_result(res, "UPLOAD cannot set TLS/SSL Verify on: ");
2608
0
  } else {
2609
0
    res = ::curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
2610
0
    check_curl_result(res, "UPLOAD cannot set TLS/SSL Verify off: ");
2611
0
  }
2612
2613
  // check to see if a CAINFO file has been specified
2614
  // command arg comes first
2615
0
  std::string const& cainfo_err = cmCurlSetCAInfo(curl, cainfo);
2616
0
  if (!cainfo_err.empty()) {
2617
0
    status.SetError(cainfo_err);
2618
0
    return false;
2619
0
  }
2620
2621
0
  cmFileCommandVectorOfChar chunkResponse;
2622
0
  cmFileCommandVectorOfChar chunkDebug;
2623
2624
0
  res = ::curl_easy_setopt(curl, CURLOPT_WRITEDATA, &chunkResponse);
2625
0
  check_curl_result(res, "UPLOAD cannot set write data: ");
2626
2627
0
  res = ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &chunkDebug);
2628
0
  check_curl_result(res, "UPLOAD cannot set debug data: ");
2629
2630
0
  res = ::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
2631
0
  check_curl_result(res, "UPLOAD cannot set follow-redirect option: ");
2632
2633
0
  if (!logVar.empty()) {
2634
0
    res = ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
2635
0
    check_curl_result(res, "UPLOAD cannot set verbose: ");
2636
0
  }
2637
2638
0
  if (timeout > 0) {
2639
0
    res = ::curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
2640
0
    check_curl_result(res, "UPLOAD cannot set timeout: ");
2641
0
  }
2642
2643
0
  if (inactivity_timeout > 0) {
2644
    // Give up if there is no progress for a long time.
2645
0
    ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1);
2646
0
    ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, inactivity_timeout);
2647
0
  }
2648
2649
  // Need the progress helper's scope to last through the duration of
2650
  // the curl_easy_perform call... so this object is declared at function
2651
  // scope intentionally, rather than inside the "if(showProgress)"
2652
  // block...
2653
  //
2654
0
  cURLProgressHelper helper(&status.GetMakefile(), "upload");
2655
2656
0
  if (showProgress) {
2657
0
    res = ::curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
2658
0
    check_curl_result(res, "UPLOAD cannot set noprogress value: ");
2659
2660
0
    res = ::curl_easy_setopt(curl, CM_CURLOPT_XFERINFOFUNCTION,
2661
0
                             cmFileUploadProgressCallback);
2662
0
    check_curl_result(res, "UPLOAD cannot set progress function: ");
2663
2664
0
    res = ::curl_easy_setopt(curl, CURLOPT_PROGRESSDATA,
2665
0
                             reinterpret_cast<void*>(&helper));
2666
0
    check_curl_result(res, "UPLOAD cannot set progress data: ");
2667
0
  }
2668
2669
  // now specify which file to upload
2670
0
  res = ::curl_easy_setopt(curl, CURLOPT_INFILE, fin);
2671
0
  check_curl_result(res, "UPLOAD cannot set input file: ");
2672
2673
  // and give the size of the upload (optional)
2674
0
  res =
2675
0
    ::curl_easy_setopt(curl, CURLOPT_INFILESIZE, static_cast<long>(file_size));
2676
0
  check_curl_result(res, "UPLOAD cannot set input file size: ");
2677
2678
0
  if (!userpwd.empty()) {
2679
0
    res = ::curl_easy_setopt(curl, CURLOPT_USERPWD, userpwd.c_str());
2680
0
    check_curl_result(res, "UPLOAD cannot set user password: ");
2681
0
  }
2682
2683
  // check to see if netrc parameters have been specified
2684
  // local command args takes precedence over CMAKE_NETRC*
2685
0
  netrc_level = cmSystemTools::UpperCase(netrc_level);
2686
0
  std::string const& netrc_option_err =
2687
0
    cmCurlSetNETRCOption(curl, netrc_level, netrc_file);
2688
0
  if (!netrc_option_err.empty()) {
2689
0
    status.SetError(netrc_option_err);
2690
0
    return false;
2691
0
  }
2692
2693
0
  struct curl_slist* headers = nullptr;
2694
0
  for (std::string const& h : curl_headers) {
2695
0
    headers = ::curl_slist_append(headers, h.c_str());
2696
0
  }
2697
0
  ::curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
2698
2699
0
  res = ::curl_easy_perform(curl);
2700
2701
0
  ::curl_slist_free_all(headers);
2702
2703
  /* always cleanup */
2704
0
  g_curl.release();
2705
0
  ::curl_easy_cleanup(curl);
2706
2707
0
  if (!statusVar.empty()) {
2708
0
    std::string m = curl_easy_strerror(res);
2709
0
    if ((res == CURLE_SSL_CONNECT_ERROR ||
2710
0
         res == CURLE_PEER_FAILED_VERIFICATION) &&
2711
0
        tlsVerifyDefaulted) {
2712
0
      m = cmStrCat(
2713
0
        std::move(m),
2714
0
        ".  If this is due to https certificate verification failure, one may "
2715
0
        "set environment variable CMAKE_TLS_VERIFY=0 to suppress it.");
2716
0
    }
2717
0
    status.GetMakefile().AddDefinition(
2718
0
      statusVar, cmStrCat(static_cast<int>(res), ";\"", std::move(m), '"'));
2719
0
  }
2720
2721
0
  ::curl_global_cleanup();
2722
2723
0
  fclose(fin);
2724
0
  fin = nullptr;
2725
2726
0
  if (!logVar.empty()) {
2727
0
    std::string log;
2728
2729
0
    if (!chunkResponse.empty()) {
2730
0
      chunkResponse.push_back(0);
2731
0
      log += "Response:\n";
2732
0
      log += chunkResponse.data();
2733
0
      log += "\n";
2734
0
    }
2735
2736
0
    if (!chunkDebug.empty()) {
2737
0
      chunkDebug.push_back(0);
2738
0
      log += "Debug:\n";
2739
0
      log += chunkDebug.data();
2740
0
      log += "\n";
2741
0
    }
2742
2743
0
    status.GetMakefile().AddDefinition(logVar, log);
2744
0
  }
2745
2746
0
  return true;
2747
#else
2748
  status.SetError("UPLOAD not supported by bootstrap cmake.");
2749
  return false;
2750
#endif
2751
0
}
2752
2753
void AddEvaluationFile(std::string const& inputName,
2754
                       std::string const& targetName,
2755
                       std::string const& outputExpr,
2756
                       std::string const& condition, bool inputIsContent,
2757
                       std::string const& newLineCharacter, mode_t permissions,
2758
                       cmExecutionStatus& status)
2759
0
{
2760
0
  cmListFileBacktrace lfbt = status.GetMakefile().GetBacktrace();
2761
2762
0
  cmGeneratorExpression outputGe(*status.GetMakefile().GetCMakeInstance(),
2763
0
                                 lfbt);
2764
0
  std::unique_ptr<cmCompiledGeneratorExpression> outputCge =
2765
0
    outputGe.Parse(outputExpr);
2766
2767
0
  cmGeneratorExpression conditionGe(*status.GetMakefile().GetCMakeInstance(),
2768
0
                                    lfbt);
2769
0
  std::unique_ptr<cmCompiledGeneratorExpression> conditionCge =
2770
0
    conditionGe.Parse(condition);
2771
2772
0
  status.GetMakefile().AddEvaluationFile(
2773
0
    inputName, targetName, std::move(outputCge), std::move(conditionCge),
2774
0
    newLineCharacter, permissions, inputIsContent);
2775
0
}
2776
2777
bool HandleGenerateCommand(std::vector<std::string> const& args,
2778
                           cmExecutionStatus& status)
2779
0
{
2780
0
  if (args.size() < 5) {
2781
0
    status.SetError("Incorrect arguments to GENERATE subcommand.");
2782
0
    return false;
2783
0
  }
2784
2785
0
  struct Arguments : public ArgumentParser::ParseResult
2786
0
  {
2787
0
    cm::optional<std::string> Output;
2788
0
    cm::optional<std::string> Input;
2789
0
    cm::optional<std::string> Content;
2790
0
    cm::optional<std::string> Condition;
2791
0
    cm::optional<std::string> Target;
2792
0
    cm::optional<std::string> NewLineStyle;
2793
0
    bool NoSourcePermissions = false;
2794
0
    bool UseSourcePermissions = false;
2795
0
    ArgumentParser::NonEmpty<std::vector<std::string>> FilePermissions;
2796
0
    std::vector<cm::string_view> ParsedKeywords;
2797
0
  };
2798
2799
0
  static auto const parser =
2800
0
    cmArgumentParser<Arguments>{}
2801
0
      .Bind("OUTPUT"_s, &Arguments::Output)
2802
0
      .Bind("INPUT"_s, &Arguments::Input)
2803
0
      .Bind("CONTENT"_s, &Arguments::Content)
2804
0
      .Bind("CONDITION"_s, &Arguments::Condition)
2805
0
      .Bind("TARGET"_s, &Arguments::Target)
2806
0
      .Bind("NO_SOURCE_PERMISSIONS"_s, &Arguments::NoSourcePermissions)
2807
0
      .Bind("USE_SOURCE_PERMISSIONS"_s, &Arguments::UseSourcePermissions)
2808
0
      .Bind("FILE_PERMISSIONS"_s, &Arguments::FilePermissions)
2809
0
      .Bind("NEWLINE_STYLE"_s, &Arguments::NewLineStyle)
2810
0
      .BindParsedKeywords(&Arguments::ParsedKeywords);
2811
2812
0
  std::vector<std::string> unparsedArguments;
2813
0
  Arguments const arguments =
2814
0
    parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments);
2815
2816
0
  if (arguments.MaybeReportError(status.GetMakefile())) {
2817
0
    return true;
2818
0
  }
2819
2820
0
  if (!unparsedArguments.empty()) {
2821
0
    status.SetError("Unknown argument to GENERATE subcommand.");
2822
0
    return false;
2823
0
  }
2824
2825
0
  if (!arguments.Output || arguments.ParsedKeywords[0] != "OUTPUT"_s) {
2826
0
    status.SetError("GENERATE requires OUTPUT as first option.");
2827
0
    return false;
2828
0
  }
2829
0
  std::string const& output = *arguments.Output;
2830
2831
0
  if (!arguments.Input && !arguments.Content) {
2832
0
    status.SetError("GENERATE requires INPUT or CONTENT option.");
2833
0
    return false;
2834
0
  }
2835
0
  bool const inputIsContent = arguments.ParsedKeywords[1] == "CONTENT"_s;
2836
0
  if (!inputIsContent && arguments.ParsedKeywords[1] != "INPUT") {
2837
0
    status.SetError("Unknown argument to GENERATE subcommand.");
2838
0
    return false;
2839
0
  }
2840
0
  std::string const& input =
2841
0
    inputIsContent ? *arguments.Content : *arguments.Input;
2842
2843
0
  if (arguments.Condition && arguments.Condition->empty()) {
2844
0
    status.SetError("CONDITION of sub-command GENERATE must not be empty "
2845
0
                    "if specified.");
2846
0
    return false;
2847
0
  }
2848
0
  std::string const& condition =
2849
0
    arguments.Condition ? *arguments.Condition : std::string();
2850
2851
0
  if (arguments.Target && arguments.Target->empty()) {
2852
0
    status.SetError("TARGET of sub-command GENERATE must not be empty "
2853
0
                    "if specified.");
2854
0
    return false;
2855
0
  }
2856
0
  std::string const& target =
2857
0
    arguments.Target ? *arguments.Target : std::string();
2858
2859
0
  cmNewLineStyle newLineStyle;
2860
0
  if (arguments.NewLineStyle) {
2861
0
    std::string errorMessage;
2862
0
    if (!newLineStyle.ReadFromArguments(args, errorMessage)) {
2863
0
      status.SetError(cmStrCat("GENERATE ", errorMessage));
2864
0
      return false;
2865
0
    }
2866
0
  }
2867
2868
0
  if (arguments.NoSourcePermissions && arguments.UseSourcePermissions) {
2869
0
    status.SetError("given both NO_SOURCE_PERMISSIONS and "
2870
0
                    "USE_SOURCE_PERMISSIONS. Only one option allowed.");
2871
0
    return false;
2872
0
  }
2873
2874
0
  if (!arguments.FilePermissions.empty()) {
2875
0
    if (arguments.NoSourcePermissions) {
2876
0
      status.SetError("given both NO_SOURCE_PERMISSIONS and "
2877
0
                      "FILE_PERMISSIONS. Only one option allowed.");
2878
0
      return false;
2879
0
    }
2880
0
    if (arguments.UseSourcePermissions) {
2881
0
      status.SetError("given both USE_SOURCE_PERMISSIONS and "
2882
0
                      "FILE_PERMISSIONS. Only one option allowed.");
2883
0
      return false;
2884
0
    }
2885
0
  }
2886
2887
0
  if (arguments.UseSourcePermissions) {
2888
0
    if (inputIsContent) {
2889
0
      status.SetError("given USE_SOURCE_PERMISSIONS without a file INPUT.");
2890
0
      return false;
2891
0
    }
2892
0
  }
2893
2894
0
  mode_t permissions = 0;
2895
0
  if (arguments.NoSourcePermissions) {
2896
0
    permissions |= cmFSPermissions::mode_owner_read;
2897
0
    permissions |= cmFSPermissions::mode_owner_write;
2898
0
    permissions |= cmFSPermissions::mode_group_read;
2899
0
    permissions |= cmFSPermissions::mode_world_read;
2900
0
  }
2901
2902
0
  if (!arguments.FilePermissions.empty()) {
2903
0
    std::vector<std::string> invalidOptions;
2904
0
    for (auto const& e : arguments.FilePermissions) {
2905
0
      if (!cmFSPermissions::stringToModeT(e, permissions)) {
2906
0
        invalidOptions.push_back(e);
2907
0
      }
2908
0
    }
2909
0
    if (!invalidOptions.empty()) {
2910
0
      std::ostringstream oss;
2911
0
      oss << "given invalid permission ";
2912
0
      for (auto i = 0u; i < invalidOptions.size(); i++) {
2913
0
        if (i == 0u) {
2914
0
          oss << "\"" << invalidOptions[i] << "\"";
2915
0
        } else {
2916
0
          oss << ",\"" << invalidOptions[i] << "\"";
2917
0
        }
2918
0
      }
2919
0
      oss << ".";
2920
0
      status.SetError(oss.str());
2921
0
      return false;
2922
0
    }
2923
0
  }
2924
2925
0
  AddEvaluationFile(input, target, output, condition, inputIsContent,
2926
0
                    newLineStyle.GetCharacters(), permissions, status);
2927
0
  return true;
2928
0
}
2929
2930
bool HandleLockCommand(std::vector<std::string> const& args,
2931
                       cmExecutionStatus& status)
2932
0
{
2933
0
#if !defined(CMAKE_BOOTSTRAP)
2934
  // Default values
2935
0
  bool directory = false;
2936
0
  bool release = false;
2937
0
  enum Guard
2938
0
  {
2939
0
    GUARD_FUNCTION,
2940
0
    GUARD_FILE,
2941
0
    GUARD_PROCESS
2942
0
  };
2943
0
  Guard guard = GUARD_PROCESS;
2944
0
  std::string resultVariable;
2945
0
  unsigned long timeout = static_cast<unsigned long>(-1);
2946
2947
  // Parse arguments
2948
0
  if (args.size() < 2) {
2949
0
    status.GetMakefile().IssueMessage(
2950
0
      MessageType::FATAL_ERROR,
2951
0
      "sub-command LOCK requires at least two arguments.");
2952
0
    return false;
2953
0
  }
2954
2955
0
  std::string path = args[1];
2956
0
  for (unsigned i = 2; i < args.size(); ++i) {
2957
0
    if (args[i] == "DIRECTORY") {
2958
0
      directory = true;
2959
0
    } else if (args[i] == "RELEASE") {
2960
0
      release = true;
2961
0
    } else if (args[i] == "GUARD") {
2962
0
      ++i;
2963
0
      char const* merr = "expected FUNCTION, FILE or PROCESS after GUARD";
2964
0
      if (i >= args.size()) {
2965
0
        status.GetMakefile().IssueMessage(MessageType::FATAL_ERROR, merr);
2966
0
        return false;
2967
0
      }
2968
0
      if (args[i] == "FUNCTION") {
2969
0
        guard = GUARD_FUNCTION;
2970
0
      } else if (args[i] == "FILE") {
2971
0
        guard = GUARD_FILE;
2972
0
      } else if (args[i] == "PROCESS") {
2973
0
        guard = GUARD_PROCESS;
2974
0
      } else {
2975
0
        status.GetMakefile().IssueMessage(
2976
0
          MessageType::FATAL_ERROR,
2977
0
          cmStrCat(merr, ", but got:\n  \"", args[i], "\"."));
2978
0
        return false;
2979
0
      }
2980
2981
0
    } else if (args[i] == "RESULT_VARIABLE") {
2982
0
      ++i;
2983
0
      if (i >= args.size()) {
2984
0
        status.GetMakefile().IssueMessage(
2985
0
          MessageType::FATAL_ERROR,
2986
0
          "expected variable name after RESULT_VARIABLE");
2987
0
        return false;
2988
0
      }
2989
0
      resultVariable = args[i];
2990
0
    } else if (args[i] == "TIMEOUT") {
2991
0
      ++i;
2992
0
      if (i >= args.size()) {
2993
0
        status.GetMakefile().IssueMessage(
2994
0
          MessageType::FATAL_ERROR, "expected timeout value after TIMEOUT");
2995
0
        return false;
2996
0
      }
2997
0
      long scanned;
2998
0
      if (!cmStrToLong(args[i], &scanned) || scanned < 0) {
2999
0
        status.GetMakefile().IssueMessage(
3000
0
          MessageType::FATAL_ERROR,
3001
0
          cmStrCat("TIMEOUT value \"", args[i],
3002
0
                   "\" is not an unsigned integer."));
3003
0
        return false;
3004
0
      }
3005
0
      timeout = static_cast<unsigned long>(scanned);
3006
0
    } else {
3007
0
      status.GetMakefile().IssueMessage(
3008
0
        MessageType::FATAL_ERROR,
3009
0
        cmStrCat("expected DIRECTORY, RELEASE, GUARD, RESULT_VARIABLE or "
3010
0
                 "TIMEOUT\nbut got: \"",
3011
0
                 args[i], "\"."));
3012
0
      return false;
3013
0
    }
3014
0
  }
3015
3016
0
  if (directory) {
3017
0
    path += "/cmake.lock";
3018
0
  }
3019
3020
  // Unify path (remove '//', '/../', ...)
3021
0
  path = cmSystemTools::CollapseFullPath(
3022
0
    path, status.GetMakefile().GetCurrentSourceDirectory());
3023
3024
  // Create file and directories if needed
3025
0
  std::string parentDir = cmSystemTools::GetParentDirectory(path);
3026
0
  if (!cmSystemTools::MakeDirectory(parentDir)) {
3027
0
    status.GetMakefile().IssueMessage(
3028
0
      MessageType::FATAL_ERROR,
3029
0
      cmStrCat("directory\n  \"", parentDir,
3030
0
               "\"\ncreation failed (check permissions)."));
3031
0
    cmSystemTools::SetFatalErrorOccurred();
3032
0
    return false;
3033
0
  }
3034
0
  FILE* file = cmsys::SystemTools::Fopen(path, "a");
3035
0
  if (!file) {
3036
0
    status.GetMakefile().IssueMessage(
3037
0
      MessageType::FATAL_ERROR,
3038
0
      cmStrCat("file\n  \"", path,
3039
0
               "\"\ncreation failed (check permissions)."));
3040
0
    cmSystemTools::SetFatalErrorOccurred();
3041
0
    return false;
3042
0
  }
3043
0
  fclose(file);
3044
3045
  // Actual lock/unlock
3046
0
  cmFileLockPool& lockPool =
3047
0
    status.GetMakefile().GetGlobalGenerator()->GetFileLockPool();
3048
3049
0
  cmFileLockResult fileLockResult(cmFileLockResult::MakeOk());
3050
0
  if (release) {
3051
0
    fileLockResult = lockPool.Release(path);
3052
0
  } else {
3053
0
    switch (guard) {
3054
0
      case GUARD_FUNCTION:
3055
0
        fileLockResult = lockPool.LockFunctionScope(path, timeout);
3056
0
        break;
3057
0
      case GUARD_FILE:
3058
0
        fileLockResult = lockPool.LockFileScope(path, timeout);
3059
0
        break;
3060
0
      case GUARD_PROCESS:
3061
0
        fileLockResult = lockPool.LockProcessScope(path, timeout);
3062
0
        break;
3063
0
      default:
3064
0
        cmSystemTools::SetFatalErrorOccurred();
3065
0
        return false;
3066
0
    }
3067
0
  }
3068
3069
0
  std::string const result = fileLockResult.GetOutputMessage();
3070
3071
0
  if (resultVariable.empty() && !fileLockResult.IsOk()) {
3072
0
    status.GetMakefile().IssueMessage(
3073
0
      MessageType::FATAL_ERROR,
3074
0
      cmStrCat("error locking file\n  \"", path, "\"\n", result, '.'));
3075
0
    cmSystemTools::SetFatalErrorOccurred();
3076
0
    return false;
3077
0
  }
3078
3079
0
  if (!resultVariable.empty()) {
3080
0
    status.GetMakefile().AddDefinition(resultVariable, result);
3081
0
  }
3082
3083
0
  return true;
3084
#else
3085
  static_cast<void>(args);
3086
  status.SetError("sub-command LOCK not implemented in bootstrap cmake");
3087
  return false;
3088
#endif
3089
0
}
3090
3091
bool HandleTimestampCommand(std::vector<std::string> const& args,
3092
                            cmExecutionStatus& status)
3093
0
{
3094
0
  if (args.size() < 3) {
3095
0
    status.SetError("sub-command TIMESTAMP requires at least two arguments.");
3096
0
    return false;
3097
0
  }
3098
0
  if (args.size() > 5) {
3099
0
    status.SetError("sub-command TIMESTAMP takes at most four arguments.");
3100
0
    return false;
3101
0
  }
3102
3103
0
  unsigned int argsIndex = 1;
3104
3105
0
  std::string filename = args[argsIndex++];
3106
0
  if (!cmsys::SystemTools::FileIsFullPath(filename)) {
3107
0
    filename = cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/',
3108
0
                        filename);
3109
0
  }
3110
3111
0
  std::string const& outputVariable = args[argsIndex++];
3112
3113
0
  std::string formatString;
3114
0
  if (args.size() > argsIndex && args[argsIndex] != "UTC") {
3115
0
    formatString = args[argsIndex++];
3116
0
  }
3117
3118
0
  bool utcFlag = false;
3119
0
  if (args.size() > argsIndex) {
3120
0
    if (args[argsIndex] == "UTC") {
3121
0
      utcFlag = true;
3122
0
    } else {
3123
0
      std::string e =
3124
0
        cmStrCat(" TIMESTAMP sub-command does not recognize option ",
3125
0
                 args[argsIndex], '.');
3126
0
      status.SetError(e);
3127
0
      return false;
3128
0
    }
3129
0
  }
3130
3131
0
  cmTimestamp timestamp;
3132
0
  std::string result =
3133
0
    timestamp.FileModificationTime(filename.c_str(), formatString, utcFlag);
3134
0
  status.GetMakefile().AddDefinition(outputVariable, result);
3135
3136
0
  return true;
3137
0
}
3138
3139
bool HandleSizeCommand(std::vector<std::string> const& args,
3140
                       cmExecutionStatus& status)
3141
0
{
3142
0
  if (args.size() != 3) {
3143
0
    status.SetError(
3144
0
      cmStrCat(args[0], " requires a file name and output variable"));
3145
0
    return false;
3146
0
  }
3147
3148
0
  unsigned int argsIndex = 1;
3149
3150
0
  std::string const& filename = args[argsIndex++];
3151
3152
0
  std::string const& outputVariable = args[argsIndex++];
3153
3154
0
  if (!cmSystemTools::FileExists(filename, true)) {
3155
0
    status.SetError(
3156
0
      cmStrCat("SIZE requested of path that is not readable:\n  ", filename));
3157
0
    return false;
3158
0
  }
3159
3160
0
  status.GetMakefile().AddDefinition(
3161
0
    outputVariable, std::to_string(cmSystemTools::FileLength(filename)));
3162
3163
0
  return true;
3164
0
}
3165
3166
bool HandleReadSymlinkCommand(std::vector<std::string> const& args,
3167
                              cmExecutionStatus& status)
3168
0
{
3169
0
  if (args.size() < 3) {
3170
0
    status.SetError(
3171
0
      cmStrCat(args[0], " requires a file name and output variable"));
3172
0
    return false;
3173
0
  }
3174
3175
0
  std::string const& filename = args[1];
3176
0
  std::string const& outputVariable = args[2];
3177
3178
0
  struct Arguments
3179
0
  {
3180
0
    std::string Result;
3181
0
  };
3182
3183
0
  static auto const parser =
3184
0
    cmArgumentParser<Arguments>{}.Bind("RESULT"_s, &Arguments::Result);
3185
3186
0
  std::vector<std::string> unconsumedArgs;
3187
0
  Arguments const arguments =
3188
0
    parser.Parse(cmMakeRange(args).advance(3), &unconsumedArgs);
3189
0
  if (!unconsumedArgs.empty()) {
3190
0
    status.SetError(
3191
0
      cmStrCat("READ_SYMLINK unknown argument:\n  ", unconsumedArgs.front()));
3192
0
    return false;
3193
0
  }
3194
3195
0
  std::string result;
3196
0
  if (!cmSystemTools::ReadSymlink(filename, result)) {
3197
0
    std::string const error = cmStrCat(
3198
0
      "READ_SYMLINK requested of path that is not a symlink:\n  ", filename);
3199
0
    if (!arguments.Result.empty()) {
3200
0
      status.GetMakefile().AddDefinition(arguments.Result, error);
3201
0
      return true;
3202
0
    }
3203
0
    status.SetError(error);
3204
0
    return false;
3205
0
  }
3206
3207
0
  status.GetMakefile().AddDefinition(outputVariable, result);
3208
0
  if (!arguments.Result.empty()) {
3209
0
    status.GetMakefile().AddDefinition(arguments.Result, "0");
3210
0
  }
3211
3212
0
  return true;
3213
0
}
3214
3215
bool HandleCreateLinkCommand(std::vector<std::string> const& args,
3216
                             cmExecutionStatus& status)
3217
0
{
3218
0
  if (args.size() < 3) {
3219
0
    status.SetError("CREATE_LINK must be called with at least two additional "
3220
0
                    "arguments");
3221
0
    return false;
3222
0
  }
3223
3224
0
  std::string const& fileName = args[1];
3225
0
  std::string const& newFileName = args[2];
3226
3227
0
  struct Arguments
3228
0
  {
3229
0
    std::string Result;
3230
0
    bool CopyOnError = false;
3231
0
    bool Symbolic = false;
3232
0
  };
3233
3234
0
  static auto const parser =
3235
0
    cmArgumentParser<Arguments>{}
3236
0
      .Bind("RESULT"_s, &Arguments::Result)
3237
0
      .Bind("COPY_ON_ERROR"_s, &Arguments::CopyOnError)
3238
0
      .Bind("SYMBOLIC"_s, &Arguments::Symbolic);
3239
3240
0
  std::vector<std::string> unconsumedArgs;
3241
0
  Arguments const arguments =
3242
0
    parser.Parse(cmMakeRange(args).advance(3), &unconsumedArgs);
3243
3244
0
  if (!unconsumedArgs.empty()) {
3245
0
    status.SetError(cmStrCat("unknown argument: \"",
3246
0
                             std::move(unconsumedArgs.front()), '\"'));
3247
0
    return false;
3248
0
  }
3249
3250
  // The system error message generated in the operation.
3251
0
  std::string result;
3252
3253
  // Check if the paths are distinct.
3254
0
  if (fileName == newFileName) {
3255
0
    result = "CREATE_LINK cannot use same file and newfile";
3256
0
    if (!arguments.Result.empty()) {
3257
0
      status.GetMakefile().AddDefinition(arguments.Result, result);
3258
0
      return true;
3259
0
    }
3260
0
    status.SetError(result);
3261
0
    return false;
3262
0
  }
3263
3264
0
  cmPolicies::PolicyStatus const cmp0205 =
3265
0
    status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0205);
3266
3267
  // Hard link requires original file to exist.
3268
0
  if (!arguments.Symbolic &&
3269
0
      (!cmSystemTools::PathExists(fileName) ||
3270
0
       (cmp0205 != cmPolicies::NEW && !cmSystemTools::FileExists(fileName)))) {
3271
0
    result =
3272
0
      cmStrCat("Cannot hard link \'", fileName, "\' as it does not exist.");
3273
0
    if (!arguments.Result.empty()) {
3274
0
      status.GetMakefile().AddDefinition(arguments.Result, result);
3275
0
      return true;
3276
0
    }
3277
0
    status.SetError(result);
3278
0
    return false;
3279
0
  }
3280
3281
  // Check if the new file already exists and remove it.
3282
0
  if (cmSystemTools::PathExists(newFileName)) {
3283
0
    cmsys::Status rmStatus;
3284
0
    if (cmp0205 == cmPolicies::NEW && arguments.CopyOnError &&
3285
0
        cmSystemTools::FileIsDirectory(newFileName) &&
3286
0
        !cmSystemTools::FileIsSymlink(newFileName)) {
3287
0
      rmStatus = cmSystemTools::RepeatedRemoveDirectory(newFileName);
3288
0
    } else {
3289
0
      rmStatus = cmSystemTools::RemoveFile(newFileName);
3290
0
    }
3291
0
    if (!rmStatus) {
3292
0
      std::string err = cmStrCat("Failed to create link '", newFileName,
3293
0
                                 "' because existing path cannot be removed: ",
3294
0
                                 rmStatus.GetString(), '\n');
3295
3296
0
      if (!arguments.Result.empty()) {
3297
0
        status.GetMakefile().AddDefinition(arguments.Result, err);
3298
0
        return true;
3299
0
      }
3300
0
      status.SetError(err);
3301
0
      return false;
3302
0
    }
3303
0
  }
3304
3305
0
  bool const sourceIsDirectory = cmSystemTools::FileIsDirectory(fileName);
3306
3307
  // Whether the operation completed successfully.
3308
0
  bool completed = false;
3309
3310
  // Check if the command requires a symbolic link.
3311
0
  if (arguments.Symbolic) {
3312
0
    cmsys::Status linked =
3313
0
      cmSystemTools::CreateSymlinkQuietly(fileName, newFileName);
3314
0
    if (linked) {
3315
0
      completed = true;
3316
0
    } else {
3317
0
      result = cmStrCat("failed to create symbolic link '", newFileName,
3318
0
                        "': ", linked.GetString());
3319
0
    }
3320
0
  } else {
3321
0
    bool needToTry = true;
3322
0
    if (sourceIsDirectory) {
3323
0
      if (cmp0205 == cmPolicies::NEW) {
3324
0
        needToTry = false;
3325
0
      } else if (cmp0205 == cmPolicies::WARN && arguments.CopyOnError) {
3326
0
        status.GetMakefile().IssueDiagnostic(
3327
0
          cmDiagnostics::CMD_AUTHOR,
3328
0
          cmStrCat("Path\n  ", fileName,
3329
0
                   "\nis a directory. Hard link creation is not supported "
3330
0
                   "for directories.\n",
3331
0
                   cmPolicies::GetPolicyWarning(cmPolicies::CMP0205)));
3332
0
      }
3333
0
    }
3334
3335
0
    if (needToTry) {
3336
0
      cmsys::Status linked =
3337
0
        cmSystemTools::CreateLinkQuietly(fileName, newFileName);
3338
0
      if (linked) {
3339
0
        completed = true;
3340
0
      } else {
3341
0
        result = cmStrCat("failed to create link '", newFileName,
3342
0
                          "': ", linked.GetString());
3343
0
      }
3344
0
    } else {
3345
0
      result =
3346
0
        cmStrCat("failed to create link '", newFileName, "': not supported");
3347
0
    }
3348
0
  }
3349
3350
0
  if (cmp0205 == cmPolicies::WARN && arguments.CopyOnError &&
3351
0
      sourceIsDirectory) {
3352
0
    status.GetMakefile().IssueDiagnostic(
3353
0
      cmDiagnostics::CMD_AUTHOR,
3354
0
      cmStrCat("Path\n  ", fileName,
3355
0
               "\nis a directory. It will be copied "
3356
0
               "recursively when CMP0205 is set to NEW.\n",
3357
0
               cmPolicies::GetPolicyWarning(cmPolicies::CMP0205)));
3358
0
  }
3359
3360
  // Check if copy-on-error is enabled in the arguments.
3361
0
  if (!completed && arguments.CopyOnError) {
3362
0
    cmsys::Status copied;
3363
0
    if (cmp0205 == cmPolicies::NEW && sourceIsDirectory) {
3364
0
      copied = cmsys::SystemTools::CopyADirectory(fileName, newFileName);
3365
0
    } else {
3366
0
      copied = cmsys::SystemTools::CopyFileAlways(fileName, newFileName);
3367
0
    }
3368
3369
0
    if (copied) {
3370
0
      completed = true;
3371
0
    } else {
3372
0
      result = "Copy failed: " + copied.GetString();
3373
0
    }
3374
0
  }
3375
3376
  // Check if the operation was successful.
3377
0
  if (completed) {
3378
0
    result = "0";
3379
0
  } else if (arguments.Result.empty()) {
3380
    // The operation failed and the result is not reported in a variable.
3381
0
    status.SetError(result);
3382
0
    return false;
3383
0
  }
3384
3385
0
  if (!arguments.Result.empty()) {
3386
0
    status.GetMakefile().AddDefinition(arguments.Result, result);
3387
0
  }
3388
3389
0
  return true;
3390
0
}
3391
3392
bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
3393
                                         cmExecutionStatus& status)
3394
0
{
3395
0
  std::string platform =
3396
0
    status.GetMakefile().GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME");
3397
0
  if (!cmRuntimeDependencyArchive::PlatformSupportsRuntimeDependencies(
3398
0
        platform)) {
3399
0
    status.SetError(
3400
0
      cmStrCat("GET_RUNTIME_DEPENDENCIES is not supported on system \"",
3401
0
               platform, '"'));
3402
0
    cmSystemTools::SetFatalErrorOccurred();
3403
0
    return false;
3404
0
  }
3405
3406
0
  if (status.GetMakefile().GetState()->GetRole() == cmState::Role::Project) {
3407
0
    status.GetMakefile().IssueDiagnostic(
3408
0
      cmDiagnostics::CMD_AUTHOR,
3409
0
      "You have used file(GET_RUNTIME_DEPENDENCIES)"
3410
0
      " in project mode. This is probably not what "
3411
0
      "you intended to do. Instead, please consider"
3412
0
      " using it in an install(CODE) or "
3413
0
      "install(SCRIPT) command. For example:"
3414
0
      "\n  install(CODE [["
3415
0
      "\n    file(GET_RUNTIME_DEPENDENCIES"
3416
0
      "\n      # ..."
3417
0
      "\n      )"
3418
0
      "\n    ]])");
3419
0
  }
3420
3421
0
  struct Arguments : public ArgumentParser::ParseResult
3422
0
  {
3423
0
    std::string ResolvedDependenciesVar;
3424
0
    std::string UnresolvedDependenciesVar;
3425
0
    std::string ConflictingDependenciesPrefix;
3426
0
    std::string RPathPrefix;
3427
0
    std::string BundleExecutable;
3428
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> Executables;
3429
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> Libraries;
3430
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> Directories;
3431
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> Modules;
3432
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> PreIncludeRegexes;
3433
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> PreExcludeRegexes;
3434
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeRegexes;
3435
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeRegexes;
3436
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeFiles;
3437
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeFiles;
3438
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>>
3439
0
      PostExcludeFilesStrict;
3440
0
  };
3441
3442
0
  static auto const parser =
3443
0
    cmArgumentParser<Arguments>{}
3444
0
      .Bind("RESOLVED_DEPENDENCIES_VAR"_s, &Arguments::ResolvedDependenciesVar)
3445
0
      .Bind("UNRESOLVED_DEPENDENCIES_VAR"_s,
3446
0
            &Arguments::UnresolvedDependenciesVar)
3447
0
      .Bind("CONFLICTING_DEPENDENCIES_PREFIX"_s,
3448
0
            &Arguments::ConflictingDependenciesPrefix)
3449
0
      .Bind("RPATH_PREFIX"_s, &Arguments::RPathPrefix)
3450
0
      .Bind("BUNDLE_EXECUTABLE"_s, &Arguments::BundleExecutable)
3451
0
      .Bind("EXECUTABLES"_s, &Arguments::Executables)
3452
0
      .Bind("LIBRARIES"_s, &Arguments::Libraries)
3453
0
      .Bind("MODULES"_s, &Arguments::Modules)
3454
0
      .Bind("DIRECTORIES"_s, &Arguments::Directories)
3455
0
      .Bind("PRE_INCLUDE_REGEXES"_s, &Arguments::PreIncludeRegexes)
3456
0
      .Bind("PRE_EXCLUDE_REGEXES"_s, &Arguments::PreExcludeRegexes)
3457
0
      .Bind("POST_INCLUDE_REGEXES"_s, &Arguments::PostIncludeRegexes)
3458
0
      .Bind("POST_EXCLUDE_REGEXES"_s, &Arguments::PostExcludeRegexes)
3459
0
      .Bind("POST_INCLUDE_FILES"_s, &Arguments::PostIncludeFiles)
3460
0
      .Bind("POST_EXCLUDE_FILES"_s, &Arguments::PostExcludeFiles)
3461
0
      .Bind("POST_EXCLUDE_FILES_STRICT"_s, &Arguments::PostExcludeFilesStrict);
3462
3463
0
  std::vector<std::string> unrecognizedArguments;
3464
0
  auto parsedArgs =
3465
0
    parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments);
3466
0
  auto argIt = unrecognizedArguments.begin();
3467
0
  if (argIt != unrecognizedArguments.end()) {
3468
0
    status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, '"'));
3469
0
    cmSystemTools::SetFatalErrorOccurred();
3470
0
    return false;
3471
0
  }
3472
3473
0
  if (parsedArgs.MaybeReportError(status.GetMakefile())) {
3474
0
    cmSystemTools::SetFatalErrorOccurred();
3475
0
    return true;
3476
0
  }
3477
3478
0
  cmRuntimeDependencyArchive archive(
3479
0
    status, parsedArgs.Directories, parsedArgs.BundleExecutable,
3480
0
    parsedArgs.PreIncludeRegexes, parsedArgs.PreExcludeRegexes,
3481
0
    parsedArgs.PostIncludeRegexes, parsedArgs.PostExcludeRegexes,
3482
0
    std::move(parsedArgs.PostIncludeFiles),
3483
0
    std::move(parsedArgs.PostExcludeFiles),
3484
0
    std::move(parsedArgs.PostExcludeFilesStrict));
3485
0
  if (!archive.Prepare()) {
3486
0
    cmSystemTools::SetFatalErrorOccurred();
3487
0
    return false;
3488
0
  }
3489
3490
0
  if (!archive.GetRuntimeDependencies(
3491
0
        parsedArgs.Executables, parsedArgs.Libraries, parsedArgs.Modules)) {
3492
0
    cmSystemTools::SetFatalErrorOccurred();
3493
0
    return false;
3494
0
  }
3495
3496
0
  std::vector<std::string> deps;
3497
0
  std::vector<std::string> unresolvedDeps;
3498
0
  std::vector<std::string> conflictingDeps;
3499
0
  for (auto const& val : archive.GetResolvedPaths()) {
3500
0
    bool unique = true;
3501
0
    auto it = val.second.begin();
3502
0
    assert(it != val.second.end());
3503
0
    auto const& firstPath = *it;
3504
0
    while (++it != val.second.end()) {
3505
0
      if (!cmSystemTools::SameFile(firstPath, *it)) {
3506
0
        unique = false;
3507
0
        break;
3508
0
      }
3509
0
    }
3510
3511
0
    if (unique) {
3512
0
      deps.push_back(firstPath);
3513
0
      if (!parsedArgs.RPathPrefix.empty()) {
3514
0
        status.GetMakefile().AddDefinition(
3515
0
          cmStrCat(parsedArgs.RPathPrefix, '_', firstPath),
3516
0
          cmList::to_string(archive.GetRPaths().at(firstPath)));
3517
0
      }
3518
0
    } else if (!parsedArgs.ConflictingDependenciesPrefix.empty()) {
3519
0
      conflictingDeps.push_back(val.first);
3520
0
      std::vector<std::string> paths;
3521
0
      paths.insert(paths.begin(), val.second.begin(), val.second.end());
3522
0
      std::string varName =
3523
0
        cmStrCat(parsedArgs.ConflictingDependenciesPrefix, '_', val.first);
3524
0
      std::string pathsStr = cmList::to_string(paths);
3525
0
      status.GetMakefile().AddDefinition(varName, pathsStr);
3526
0
    } else {
3527
0
      std::ostringstream e;
3528
0
      e << "Multiple conflicting paths found for " << val.first << ":";
3529
0
      for (auto const& path : val.second) {
3530
0
        e << "\n  " << path;
3531
0
      }
3532
0
      status.SetError(e.str());
3533
0
      cmSystemTools::SetFatalErrorOccurred();
3534
0
      return false;
3535
0
    }
3536
0
  }
3537
0
  if (!archive.GetUnresolvedPaths().empty()) {
3538
0
    if (!parsedArgs.UnresolvedDependenciesVar.empty()) {
3539
0
      unresolvedDeps.insert(unresolvedDeps.begin(),
3540
0
                            archive.GetUnresolvedPaths().begin(),
3541
0
                            archive.GetUnresolvedPaths().end());
3542
0
    } else {
3543
0
      std::ostringstream e;
3544
0
      e << "Could not resolve runtime dependencies:";
3545
0
      for (auto const& path : archive.GetUnresolvedPaths()) {
3546
0
        e << "\n  " << path;
3547
0
      }
3548
0
      status.SetError(e.str());
3549
0
      cmSystemTools::SetFatalErrorOccurred();
3550
0
      return false;
3551
0
    }
3552
0
  }
3553
3554
0
  if (!parsedArgs.ResolvedDependenciesVar.empty()) {
3555
0
    std::string val = cmList::to_string(deps);
3556
0
    status.GetMakefile().AddDefinition(parsedArgs.ResolvedDependenciesVar,
3557
0
                                       val);
3558
0
  }
3559
0
  if (!parsedArgs.UnresolvedDependenciesVar.empty()) {
3560
0
    std::string val = cmList::to_string(unresolvedDeps);
3561
0
    status.GetMakefile().AddDefinition(parsedArgs.UnresolvedDependenciesVar,
3562
0
                                       val);
3563
0
  }
3564
0
  if (!parsedArgs.ConflictingDependenciesPrefix.empty()) {
3565
0
    std::string val = cmList::to_string(conflictingDeps);
3566
0
    status.GetMakefile().AddDefinition(
3567
0
      parsedArgs.ConflictingDependenciesPrefix + "_FILENAMES", val);
3568
0
  }
3569
0
  return true;
3570
0
}
3571
3572
bool HandleConfigureCommand(std::vector<std::string> const& args,
3573
                            cmExecutionStatus& status)
3574
0
{
3575
0
  struct Arguments : public ArgumentParser::ParseResult
3576
0
  {
3577
0
    cm::optional<std::string> Output;
3578
0
    cm::optional<std::string> Content;
3579
0
    bool EscapeQuotes = false;
3580
0
    bool AtOnly = false;
3581
    // "NEWLINE_STYLE" requires one value, but we use a custom check below.
3582
0
    ArgumentParser::Maybe<std::string> NewlineStyle;
3583
0
  };
3584
3585
0
  static auto const parser =
3586
0
    cmArgumentParser<Arguments>{}
3587
0
      .Bind("OUTPUT"_s, &Arguments::Output)
3588
0
      .Bind("CONTENT"_s, &Arguments::Content)
3589
0
      .Bind("ESCAPE_QUOTES"_s, &Arguments::EscapeQuotes)
3590
0
      .Bind("@ONLY"_s, &Arguments::AtOnly)
3591
0
      .Bind("NEWLINE_STYLE"_s, &Arguments::NewlineStyle);
3592
3593
0
  std::vector<std::string> unrecognizedArguments;
3594
0
  auto parsedArgs =
3595
0
    parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments);
3596
3597
0
  auto argIt = unrecognizedArguments.begin();
3598
0
  if (argIt != unrecognizedArguments.end()) {
3599
0
    status.SetError(
3600
0
      cmStrCat("CONFIGURE Unrecognized argument: \"", *argIt, '"'));
3601
0
    cmSystemTools::SetFatalErrorOccurred();
3602
0
    return false;
3603
0
  }
3604
3605
0
  if (parsedArgs.MaybeReportError(status.GetMakefile())) {
3606
0
    cmSystemTools::SetFatalErrorOccurred();
3607
0
    return true;
3608
0
  }
3609
3610
0
  if (!parsedArgs.Output) {
3611
0
    status.SetError("CONFIGURE OUTPUT option is mandatory.");
3612
0
    cmSystemTools::SetFatalErrorOccurred();
3613
0
    return false;
3614
0
  }
3615
0
  if (!parsedArgs.Content) {
3616
0
    status.SetError("CONFIGURE CONTENT option is mandatory.");
3617
0
    cmSystemTools::SetFatalErrorOccurred();
3618
0
    return false;
3619
0
  }
3620
3621
0
  std::string errorMessage;
3622
0
  cmNewLineStyle newLineStyle;
3623
0
  if (!newLineStyle.ReadFromArguments(args, errorMessage)) {
3624
0
    status.SetError(cmStrCat("CONFIGURE ", errorMessage));
3625
0
    return false;
3626
0
  }
3627
3628
  // Check for generator expressions
3629
0
  std::string outputFile = cmSystemTools::CollapseFullPath(
3630
0
    *parsedArgs.Output, status.GetMakefile().GetCurrentBinaryDirectory());
3631
3632
0
  std::string::size_type pos = outputFile.find_first_of("<>");
3633
0
  if (pos != std::string::npos) {
3634
0
    status.SetError(cmStrCat("CONFIGURE called with OUTPUT containing a \"",
3635
0
                             outputFile[pos],
3636
0
                             "\".  This character is not allowed."));
3637
0
    return false;
3638
0
  }
3639
3640
0
  cmMakefile& makeFile = status.GetMakefile();
3641
0
  if (!makeFile.CanIWriteThisFile(outputFile)) {
3642
0
    cmSystemTools::Error("Attempt to write file: " + outputFile +
3643
0
                         " into a source directory.");
3644
0
    return false;
3645
0
  }
3646
3647
0
  cmSystemTools::ConvertToUnixSlashes(outputFile);
3648
3649
  // Re-generate if non-temporary outputs are missing.
3650
  // when we finalize the configuration we will remove all
3651
  // output files that now don't exist.
3652
0
  makeFile.AddCMakeOutputFile(outputFile);
3653
3654
  // Create output directory
3655
0
  std::string::size_type const slashPos = outputFile.rfind('/');
3656
0
  if (slashPos != std::string::npos) {
3657
0
    std::string const path = outputFile.substr(0, slashPos);
3658
0
    cmSystemTools::MakeDirectory(path);
3659
0
  }
3660
3661
0
  std::string newLineCharacters = "\n";
3662
0
  bool open_with_binary_flag = false;
3663
0
  if (newLineStyle.IsValid()) {
3664
0
    newLineCharacters = newLineStyle.GetCharacters();
3665
0
    open_with_binary_flag = true;
3666
0
  }
3667
3668
0
  cmGeneratedFileStream fout;
3669
0
  fout.Open(outputFile, false, open_with_binary_flag);
3670
0
  if (!fout) {
3671
0
    cmSystemTools::Error("Could not open file for write in copy operation " +
3672
0
                         outputFile);
3673
0
    cmSystemTools::ReportLastSystemError("");
3674
0
    return false;
3675
0
  }
3676
0
  fout.SetCopyIfDifferent(true);
3677
3678
  // copy input to output and expand variables from input at the same time
3679
0
  std::stringstream sin(*parsedArgs.Content, std::ios::in);
3680
0
  std::string inLine;
3681
0
  std::string outLine;
3682
0
  bool hasNewLine = false;
3683
0
  while (cmSystemTools::GetLineFromStream(sin, inLine, &hasNewLine)) {
3684
0
    outLine.clear();
3685
0
    makeFile.ConfigureString(inLine, outLine, parsedArgs.AtOnly,
3686
0
                             parsedArgs.EscapeQuotes);
3687
0
    fout << outLine;
3688
0
    if (hasNewLine || newLineStyle.IsValid()) {
3689
0
      fout << newLineCharacters;
3690
0
    }
3691
0
  }
3692
3693
  // close file before attempting to copy
3694
0
  fout.close();
3695
3696
0
  return true;
3697
0
}
3698
3699
bool HandleArchiveCreateCommand(std::vector<std::string> const& args,
3700
                                cmExecutionStatus& status)
3701
0
{
3702
0
  struct Arguments : public ArgumentParser::ParseResult
3703
0
  {
3704
0
    std::string Output;
3705
0
    std::string Format;
3706
0
    std::string Compression;
3707
0
    std::string CompressionLevel;
3708
0
    std::string Encoding;
3709
    // "MTIME" should require one value, but it has long been accidentally
3710
    // accepted without one and treated as if an empty value were given.
3711
    // Fixing this would require a policy.
3712
0
    ArgumentParser::Maybe<std::string> MTime;
3713
0
    std::string Threads;
3714
0
    std::string WorkingDirectory;
3715
0
    bool Verbose = false;
3716
    // "PATHS" requires at least one value, but use a custom check below.
3717
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> Paths;
3718
0
  };
3719
3720
0
  static auto const parser =
3721
0
    cmArgumentParser<Arguments>{}
3722
0
      .Bind("OUTPUT"_s, &Arguments::Output)
3723
0
      .Bind("FORMAT"_s, &Arguments::Format)
3724
0
      .Bind("COMPRESSION"_s, &Arguments::Compression)
3725
0
      .Bind("COMPRESSION_LEVEL"_s, &Arguments::CompressionLevel)
3726
0
      .Bind("ENCODING"_s, &Arguments::Encoding)
3727
0
      .Bind("MTIME"_s, &Arguments::MTime)
3728
0
      .Bind("THREADS"_s, &Arguments::Threads)
3729
0
      .Bind("WORKING_DIRECTORY"_s, &Arguments::WorkingDirectory)
3730
0
      .Bind("VERBOSE"_s, &Arguments::Verbose)
3731
0
      .Bind("PATHS"_s, &Arguments::Paths);
3732
3733
0
  std::vector<std::string> unrecognizedArguments;
3734
0
  auto parsedArgs =
3735
0
    parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments);
3736
0
  auto argIt = unrecognizedArguments.begin();
3737
0
  if (argIt != unrecognizedArguments.end()) {
3738
0
    status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, '"'));
3739
0
    cmSystemTools::SetFatalErrorOccurred();
3740
0
    return false;
3741
0
  }
3742
3743
0
  if (parsedArgs.MaybeReportError(status.GetMakefile())) {
3744
0
    cmSystemTools::SetFatalErrorOccurred();
3745
0
    return true;
3746
0
  }
3747
3748
0
  char const* knownFormats[] = {
3749
0
    "7zip", "gnutar", "pax", "paxr", "raw", "zip"
3750
0
  };
3751
3752
0
  if (!parsedArgs.Format.empty() &&
3753
0
      !cm::contains(knownFormats, parsedArgs.Format)) {
3754
0
    status.SetError(
3755
0
      cmStrCat("archive format ", parsedArgs.Format, " not supported"));
3756
0
    cmSystemTools::SetFatalErrorOccurred();
3757
0
    return false;
3758
0
  }
3759
3760
0
  static std::map<std::string, cmSystemTools::cmTarCompression>
3761
0
    compressionTypeMap = { { "None", cmSystemTools::TarCompressNone },
3762
0
                           { "BZip2", cmSystemTools::TarCompressBZip2 },
3763
0
                           { "Deflate", cmSystemTools::TarCompressGZip },
3764
0
                           { "GZip", cmSystemTools::TarCompressGZip },
3765
0
                           { "LZMA", cmSystemTools::TarCompressLZMA },
3766
0
                           { "LZMA2", cmSystemTools::TarCompressXZ },
3767
0
                           { "PPMd", cmSystemTools::TarCompressPPMd },
3768
0
                           { "XZ", cmSystemTools::TarCompressXZ },
3769
0
                           { "Zstd", cmSystemTools::TarCompressZstd } };
3770
3771
0
  cmSystemTools::cmTarCompression compress = cmSystemTools::TarCompressAuto;
3772
0
  auto typeIt = compressionTypeMap.find(parsedArgs.Compression);
3773
0
  if (typeIt != compressionTypeMap.end()) {
3774
0
    compress = typeIt->second;
3775
0
  } else if (!parsedArgs.Compression.empty()) {
3776
0
    status.SetError(cmStrCat("compression type ", parsedArgs.Compression,
3777
0
                             " is not supported"));
3778
0
    cmSystemTools::SetFatalErrorOccurred();
3779
0
    return false;
3780
0
  }
3781
3782
0
  if (compress == cmSystemTools::TarCompressPPMd &&
3783
0
      parsedArgs.Format != "7zip") {
3784
0
    status.SetError(cmStrCat("PPMd compression is not supported for ",
3785
0
                             parsedArgs.Format, " format"));
3786
0
    cmSystemTools::SetFatalErrorOccurred();
3787
0
    return false;
3788
0
  }
3789
3790
0
  int compressionLevel = 0;
3791
0
  constexpr int minCompressionLevel = 0;
3792
0
  int maxCompressionLevel = 9;
3793
0
  if (compress == cmSystemTools::TarCompressZstd &&
3794
0
      parsedArgs.Format != "zip") {
3795
0
    maxCompressionLevel = 19;
3796
0
  }
3797
3798
0
  if (!parsedArgs.CompressionLevel.empty()) {
3799
0
    if (parsedArgs.CompressionLevel.size() != 1 &&
3800
0
        !cmsysString_isdigit(parsedArgs.CompressionLevel[0])) {
3801
0
      status.SetError(
3802
0
        cmStrCat("compression level ", parsedArgs.CompressionLevel, " for ",
3803
0
                 parsedArgs.Compression, " should be in range ",
3804
0
                 minCompressionLevel, " to ", maxCompressionLevel));
3805
0
      cmSystemTools::SetFatalErrorOccurred();
3806
0
      return false;
3807
0
    }
3808
0
    compressionLevel = std::stoi(parsedArgs.CompressionLevel);
3809
0
    if (compressionLevel < minCompressionLevel ||
3810
0
        compressionLevel > maxCompressionLevel) {
3811
0
      status.SetError(
3812
0
        cmStrCat("compression level ", parsedArgs.CompressionLevel, " for ",
3813
0
                 parsedArgs.Compression, " should be in range ",
3814
0
                 minCompressionLevel, " to ", maxCompressionLevel));
3815
0
      cmSystemTools::SetFatalErrorOccurred();
3816
0
      return false;
3817
0
    }
3818
0
    if (compress == cmSystemTools::TarCompressNone) {
3819
0
      status.SetError(
3820
0
        cmStrCat("compression level is not supported for "
3821
0
                 "compression \"None\". Provided compression level: ",
3822
0
                 parsedArgs.CompressionLevel));
3823
0
      cmSystemTools::SetFatalErrorOccurred();
3824
0
      return false;
3825
0
    }
3826
0
  }
3827
3828
  // Use the single thread by default for backward compatibility
3829
0
  int threads = 1;
3830
0
  constexpr int minThreads = 0;
3831
0
  if (!parsedArgs.Threads.empty()) {
3832
0
    if (parsedArgs.Threads.size() != 1 &&
3833
0
        !cmsysString_isdigit(parsedArgs.Threads[0])) {
3834
0
      status.SetError(cmStrCat("number of threads ", parsedArgs.Threads,
3835
0
                               " should be at least ", minThreads));
3836
0
      cmSystemTools::SetFatalErrorOccurred();
3837
0
      return false;
3838
0
    }
3839
0
    threads = std::stoi(parsedArgs.Threads);
3840
0
    if (threads < minThreads) {
3841
0
      status.SetError(cmStrCat("number of threads ", parsedArgs.Threads,
3842
0
                               " should be at least ", minThreads));
3843
0
      cmSystemTools::SetFatalErrorOccurred();
3844
0
      return false;
3845
0
    }
3846
0
  }
3847
3848
0
  if (parsedArgs.Paths.empty()) {
3849
0
    status.SetError("ARCHIVE_CREATE requires a non-empty list of PATHS");
3850
0
    cmSystemTools::SetFatalErrorOccurred();
3851
0
    return false;
3852
0
  }
3853
3854
0
  if (parsedArgs.Encoding.empty()) {
3855
0
    if (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0213) ==
3856
0
        cmPolicies::NEW) {
3857
0
      parsedArgs.Encoding = "UTF-8";
3858
0
    } else {
3859
0
      parsedArgs.Encoding = "OEM";
3860
0
    }
3861
0
  }
3862
3863
0
  if (!cmSystemTools::CreateTar(
3864
0
        parsedArgs.Output, parsedArgs.Paths, parsedArgs.WorkingDirectory,
3865
0
        compress, parsedArgs.Encoding, parsedArgs.Verbose, parsedArgs.MTime,
3866
0
        parsedArgs.Format, compressionLevel, threads)) {
3867
0
    status.SetError(cmStrCat("failed to compress: ", parsedArgs.Output));
3868
0
    cmSystemTools::SetFatalErrorOccurred();
3869
0
    return false;
3870
0
  }
3871
3872
0
  return true;
3873
0
}
3874
3875
bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
3876
                                 cmExecutionStatus& status)
3877
0
{
3878
0
  struct Arguments : public ArgumentParser::ParseResult
3879
0
  {
3880
0
    std::string Input;
3881
0
    std::string Encoding;
3882
0
    bool Verbose = false;
3883
0
    bool ListOnly = false;
3884
0
    std::string Destination;
3885
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> Patterns;
3886
0
    bool Touch = false;
3887
0
  };
3888
3889
0
  static auto const parser = cmArgumentParser<Arguments>{}
3890
0
                               .Bind("INPUT"_s, &Arguments::Input)
3891
0
                               .Bind("ENCODING"_s, &Arguments::Encoding)
3892
0
                               .Bind("VERBOSE"_s, &Arguments::Verbose)
3893
0
                               .Bind("LIST_ONLY"_s, &Arguments::ListOnly)
3894
0
                               .Bind("DESTINATION"_s, &Arguments::Destination)
3895
0
                               .Bind("PATTERNS"_s, &Arguments::Patterns)
3896
0
                               .Bind("TOUCH"_s, &Arguments::Touch);
3897
3898
0
  std::vector<std::string> unrecognizedArguments;
3899
0
  auto parsedArgs =
3900
0
    parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments);
3901
0
  auto argIt = unrecognizedArguments.begin();
3902
0
  if (argIt != unrecognizedArguments.end()) {
3903
0
    status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, '"'));
3904
0
    cmSystemTools::SetFatalErrorOccurred();
3905
0
    return false;
3906
0
  }
3907
3908
0
  if (parsedArgs.MaybeReportError(status.GetMakefile())) {
3909
0
    cmSystemTools::SetFatalErrorOccurred();
3910
0
    return true;
3911
0
  }
3912
3913
0
  std::string inFile = parsedArgs.Input;
3914
3915
0
  if (parsedArgs.Encoding.empty()) {
3916
0
    if (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0213) ==
3917
0
        cmPolicies::NEW) {
3918
0
      parsedArgs.Encoding = "UTF-8";
3919
0
    } else {
3920
0
      parsedArgs.Encoding = "OEM";
3921
0
    }
3922
0
  }
3923
3924
0
  if (parsedArgs.ListOnly) {
3925
0
    if (!cmSystemTools::ListTar(inFile, parsedArgs.Patterns,
3926
0
                                parsedArgs.Encoding, parsedArgs.Verbose)) {
3927
0
      status.SetError(cmStrCat("failed to list: ", inFile));
3928
0
      cmSystemTools::SetFatalErrorOccurred();
3929
0
      return false;
3930
0
    }
3931
0
  } else {
3932
0
    std::string destDir = status.GetMakefile().GetCurrentBinaryDirectory();
3933
0
    if (!parsedArgs.Destination.empty()) {
3934
0
      if (cmSystemTools::FileIsFullPath(parsedArgs.Destination)) {
3935
0
        destDir = parsedArgs.Destination;
3936
0
      } else {
3937
0
        destDir = cmStrCat(destDir, '/', parsedArgs.Destination);
3938
0
      }
3939
3940
0
      if (!cmSystemTools::MakeDirectory(destDir)) {
3941
0
        status.SetError(cmStrCat("failed to create directory: ", destDir));
3942
0
        cmSystemTools::SetFatalErrorOccurred();
3943
0
        return false;
3944
0
      }
3945
3946
0
      if (!cmSystemTools::FileIsFullPath(inFile)) {
3947
0
        inFile =
3948
0
          cmStrCat(cmSystemTools::GetLogicalWorkingDirectory(), '/', inFile);
3949
0
      }
3950
0
    }
3951
3952
0
    cmWorkingDirectory workdir(destDir);
3953
0
    if (workdir.Failed()) {
3954
0
      status.SetError(workdir.GetError());
3955
0
      cmSystemTools::SetFatalErrorOccurred();
3956
0
      return false;
3957
0
    }
3958
3959
0
    if (!cmSystemTools::ExtractTar(
3960
0
          inFile, parsedArgs.Patterns,
3961
0
          parsedArgs.Touch ? cmSystemTools::cmTarExtractTimestamps::No
3962
0
                           : cmSystemTools::cmTarExtractTimestamps::Yes,
3963
0
          parsedArgs.Encoding, parsedArgs.Verbose)) {
3964
0
      status.SetError(cmStrCat("failed to extract:\n  ", inFile));
3965
0
      cmSystemTools::SetFatalErrorOccurred();
3966
0
      return false;
3967
0
    }
3968
0
  }
3969
3970
0
  return true;
3971
0
}
3972
3973
bool ValidateAndConvertPermissions(
3974
  cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> const&
3975
    permissions,
3976
  mode_t& perms, cmExecutionStatus& status)
3977
0
{
3978
0
  if (!permissions) {
3979
0
    return true;
3980
0
  }
3981
0
  for (auto const& i : *permissions) {
3982
0
    if (!cmFSPermissions::stringToModeT(i, perms)) {
3983
0
      status.SetError(i + " is an invalid permission specifier");
3984
0
      cmSystemTools::SetFatalErrorOccurred();
3985
0
      return false;
3986
0
    }
3987
0
  }
3988
0
  return true;
3989
0
}
3990
3991
bool SetPermissions(std::string const& filename, mode_t perms,
3992
                    cmExecutionStatus& status)
3993
0
{
3994
0
  if (!cmSystemTools::SetPermissions(filename, perms)) {
3995
0
    status.SetError("Failed to set permissions for " + filename);
3996
0
    cmSystemTools::SetFatalErrorOccurred();
3997
0
    return false;
3998
0
  }
3999
0
  return true;
4000
0
}
4001
4002
bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse,
4003
                            cmExecutionStatus& status)
4004
0
{
4005
0
  mode_t perms = 0;
4006
0
  mode_t fperms = 0;
4007
0
  mode_t dperms = 0;
4008
0
  cmsys::Glob globber;
4009
4010
0
  globber.SetRecurse(recurse);
4011
0
  globber.SetRecurseListDirs(recurse);
4012
4013
0
  struct Arguments : public ArgumentParser::ParseResult
4014
0
  {
4015
0
    cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>>
4016
0
      Permissions;
4017
0
    cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>>
4018
0
      FilePermissions;
4019
0
    cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>>
4020
0
      DirectoryPermissions;
4021
0
  };
4022
4023
0
  static auto const parser =
4024
0
    cmArgumentParser<Arguments>{}
4025
0
      .Bind("PERMISSIONS"_s, &Arguments::Permissions)
4026
0
      .Bind("FILE_PERMISSIONS"_s, &Arguments::FilePermissions)
4027
0
      .Bind("DIRECTORY_PERMISSIONS"_s, &Arguments::DirectoryPermissions);
4028
4029
0
  std::vector<std::string> pathEntries;
4030
0
  Arguments parsedArgs =
4031
0
    parser.Parse(cmMakeRange(args).advance(1), &pathEntries);
4032
4033
  // check validity of arguments
4034
0
  if (!parsedArgs.Permissions && !parsedArgs.FilePermissions &&
4035
0
      !parsedArgs.DirectoryPermissions) // no permissions given
4036
0
  {
4037
0
    status.SetError("No permissions given");
4038
0
    cmSystemTools::SetFatalErrorOccurred();
4039
0
    return false;
4040
0
  }
4041
4042
0
  if (parsedArgs.Permissions && parsedArgs.FilePermissions &&
4043
0
      parsedArgs.DirectoryPermissions) // all keywords are used
4044
0
  {
4045
0
    status.SetError("Remove either PERMISSIONS or FILE_PERMISSIONS or "
4046
0
                    "DIRECTORY_PERMISSIONS from the invocation");
4047
0
    cmSystemTools::SetFatalErrorOccurred();
4048
0
    return false;
4049
0
  }
4050
4051
0
  if (parsedArgs.MaybeReportError(status.GetMakefile())) {
4052
0
    cmSystemTools::SetFatalErrorOccurred();
4053
0
    return true;
4054
0
  }
4055
4056
  // validate permissions
4057
0
  bool validatePermissions =
4058
0
    ValidateAndConvertPermissions(parsedArgs.Permissions, perms, status) &&
4059
0
    ValidateAndConvertPermissions(parsedArgs.FilePermissions, fperms,
4060
0
                                  status) &&
4061
0
    ValidateAndConvertPermissions(parsedArgs.DirectoryPermissions, dperms,
4062
0
                                  status);
4063
0
  if (!validatePermissions) {
4064
0
    return false;
4065
0
  }
4066
4067
0
  std::vector<std::string> allPathEntries;
4068
4069
0
  if (recurse) {
4070
0
    std::vector<std::string> tempPathEntries;
4071
0
    for (auto const& i : pathEntries) {
4072
0
      if (cmSystemTools::FileIsDirectory(i)) {
4073
0
        globber.FindFiles(i + "/*");
4074
0
        tempPathEntries = globber.GetFiles();
4075
0
        allPathEntries.insert(allPathEntries.end(), tempPathEntries.begin(),
4076
0
                              tempPathEntries.end());
4077
0
        allPathEntries.emplace_back(i);
4078
0
      } else {
4079
0
        allPathEntries.emplace_back(i); // We validate path entries below
4080
0
      }
4081
0
    }
4082
0
  } else {
4083
0
    allPathEntries = std::move(pathEntries);
4084
0
  }
4085
4086
  // chmod
4087
0
  for (auto const& i : allPathEntries) {
4088
0
    if (!(cmSystemTools::FileExists(i) || cmSystemTools::FileIsDirectory(i))) {
4089
0
      status.SetError(cmStrCat("does not exist:\n  ", i));
4090
0
      cmSystemTools::SetFatalErrorOccurred();
4091
0
      return false;
4092
0
    }
4093
4094
0
    if (cmSystemTools::FileExists(i, true)) {
4095
0
      bool success = true;
4096
0
      mode_t filePermissions = parsedArgs.FilePermissions ? fperms : perms;
4097
0
      if (filePermissions) {
4098
0
        success = SetPermissions(i, filePermissions, status);
4099
0
      }
4100
0
      if (!success) {
4101
0
        return false;
4102
0
      }
4103
0
    }
4104
4105
0
    else if (cmSystemTools::FileIsDirectory(i)) {
4106
0
      bool success = true;
4107
0
      mode_t directoryPermissions =
4108
0
        parsedArgs.DirectoryPermissions ? dperms : perms;
4109
0
      if (directoryPermissions) {
4110
0
        success = SetPermissions(i, directoryPermissions, status);
4111
0
      }
4112
0
      if (!success) {
4113
0
        return false;
4114
0
      }
4115
0
    }
4116
0
  }
4117
4118
0
  return true;
4119
0
}
4120
4121
bool HandleChmodCommand(std::vector<std::string> const& args,
4122
                        cmExecutionStatus& status)
4123
0
{
4124
0
  return HandleChmodCommandImpl(args, false, status);
4125
0
}
4126
4127
bool HandleChmodRecurseCommand(std::vector<std::string> const& args,
4128
                               cmExecutionStatus& status)
4129
0
{
4130
0
  return HandleChmodCommandImpl(args, true, status);
4131
0
}
4132
4133
} // namespace
4134
4135
bool cmFileCommand(std::vector<std::string> const& args,
4136
                   cmExecutionStatus& status)
4137
0
{
4138
0
  if (args.empty()) {
4139
0
    status.SetError(
4140
0
      "given no arguments, but it requires at least a sub-command.");
4141
0
    return false;
4142
0
  }
4143
4144
0
  static cmSubcommandTable const subcommand{
4145
0
    { "WRITE"_s, HandleWriteCommand },
4146
0
    { "APPEND"_s, HandleAppendCommand },
4147
0
    { "DOWNLOAD"_s, HandleDownloadCommand },
4148
0
    { "UPLOAD"_s, HandleUploadCommand },
4149
0
    { "READ"_s, HandleReadCommand },
4150
0
    { "MD5"_s, HandleHashCommand },
4151
0
    { "SHA1"_s, HandleHashCommand },
4152
0
    { "SHA224"_s, HandleHashCommand },
4153
0
    { "SHA256"_s, HandleHashCommand },
4154
0
    { "SHA384"_s, HandleHashCommand },
4155
0
    { "SHA512"_s, HandleHashCommand },
4156
0
    { "SHA3_224"_s, HandleHashCommand },
4157
0
    { "SHA3_256"_s, HandleHashCommand },
4158
0
    { "SHA3_384"_s, HandleHashCommand },
4159
0
    { "SHA3_512"_s, HandleHashCommand },
4160
0
    { "STRINGS"_s, HandleStringsCommand },
4161
0
    { "GLOB"_s, HandleGlobCommand },
4162
0
    { "GLOB_RECURSE"_s, HandleGlobRecurseCommand },
4163
0
    { "MAKE_DIRECTORY"_s, HandleMakeDirectoryCommand },
4164
0
    { "RENAME"_s, HandleRename },
4165
0
    { "COPY_FILE"_s, HandleCopyFile },
4166
0
    { "REMOVE"_s, HandleRemove },
4167
0
    { "REMOVE_RECURSE"_s, HandleRemoveRecurse },
4168
0
    { "COPY"_s, HandleCopyCommand },
4169
0
    { "INSTALL"_s, HandleInstallCommand },
4170
0
    { "DIFFERENT"_s, HandleDifferentCommand },
4171
0
    { "RPATH_CHANGE"_s, HandleRPathChangeCommand },
4172
0
    { "CHRPATH"_s, HandleRPathChangeCommand },
4173
0
    { "RPATH_SET"_s, HandleRPathSetCommand },
4174
0
    { "RPATH_CHECK"_s, HandleRPathCheckCommand },
4175
0
    { "RPATH_REMOVE"_s, HandleRPathRemoveCommand },
4176
0
    { "READ_ELF"_s, HandleReadElfCommand },
4177
0
    { "READ_MACHO"_s, HandleReadMachoCommand },
4178
0
    { "REAL_PATH"_s, HandleRealPathCommand },
4179
0
    { "RELATIVE_PATH"_s, HandleRelativePathCommand },
4180
0
    { "TO_CMAKE_PATH"_s, HandleCMakePathCommand },
4181
0
    { "TO_NATIVE_PATH"_s, HandleNativePathCommand },
4182
0
    { "TOUCH"_s, HandleTouchCommand },
4183
0
    { "TOUCH_NOCREATE"_s, HandleTouchNocreateCommand },
4184
0
    { "TIMESTAMP"_s, HandleTimestampCommand },
4185
0
    { "GENERATE"_s, HandleGenerateCommand },
4186
0
    { "LOCK"_s, HandleLockCommand },
4187
0
    { "SIZE"_s, HandleSizeCommand },
4188
0
    { "READ_SYMLINK"_s, HandleReadSymlinkCommand },
4189
0
    { "CREATE_LINK"_s, HandleCreateLinkCommand },
4190
0
    { "GET_RUNTIME_DEPENDENCIES"_s, HandleGetRuntimeDependenciesCommand },
4191
0
    { "CONFIGURE"_s, HandleConfigureCommand },
4192
0
    { "ARCHIVE_CREATE"_s, HandleArchiveCreateCommand },
4193
0
    { "ARCHIVE_EXTRACT"_s, HandleArchiveExtractCommand },
4194
0
    { "CHMOD"_s, HandleChmodCommand },
4195
0
    { "CHMOD_RECURSE"_s, HandleChmodRecurseCommand },
4196
0
  };
4197
4198
0
  return subcommand(args[0], args, status);
4199
0
}