Coverage Report

Created: 2026-04-29 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmake.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 "cmake.h"
4
5
#include <algorithm>
6
#include <array>
7
#include <cassert>
8
#include <chrono>
9
#include <climits>
10
#include <cstdio>
11
#include <cstdlib>
12
#include <initializer_list>
13
#include <iomanip>
14
#include <iostream>
15
#include <iterator>
16
#include <sstream>
17
#include <stdexcept>
18
#include <utility>
19
20
#include <cm/memory>
21
#include <cm/optional>
22
#include <cm/string_view>
23
#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(CMAKE_BOOT_MINGW)
24
#  include <cm/iterator>
25
#endif
26
27
#include <cmext/algorithm>
28
#include <cmext/string_view>
29
30
#include <sys/types.h>
31
32
#include "cmsys/FStream.hxx"
33
#include "cmsys/Glob.hxx"
34
#include "cmsys/RegularExpression.hxx"
35
36
#include "cm_sys_stat.h"
37
38
#include "cmBuildOptions.h"
39
#include "cmCMakePath.h"
40
#include "cmCMakePresetsGraph.h"
41
#include "cmCommandLineArgument.h"
42
#include "cmCommands.h"
43
#ifdef CMake_ENABLE_DEBUGGER
44
#  include "cmDebuggerAdapter.h"
45
#  ifdef _WIN32
46
#    include "cmDebuggerWindowsPipeConnection.h"
47
#  else //!_WIN32
48
#    include "cmDebuggerPosixPipeConnection.h"
49
#  endif //_WIN32
50
#endif
51
#include "cmDocumentation.h"
52
#include "cmDocumentationEntry.h"
53
#include "cmDuration.h"
54
#include "cmExternalMakefileProjectGenerator.h"
55
#include "cmFileTimeCache.h"
56
#include "cmGeneratorTarget.h"
57
#include "cmGlobCacheEntry.h" // IWYU pragma: keep
58
#include "cmGlobalGenerator.h"
59
#include "cmGlobalGeneratorFactory.h"
60
#include "cmLinkLineComputer.h"
61
#include "cmLocalGenerator.h"
62
#include "cmMakefile.h"
63
#if !defined(CMAKE_BOOTSTRAP)
64
#  include "cmMakefileProfilingData.h"
65
#endif
66
#include "cmJSONState.h"
67
#include "cmList.h"
68
#include "cmMessenger.h"
69
#ifndef CMAKE_BOOTSTRAP
70
#  include "cmSarifLog.h"
71
#endif
72
#include "cmState.h"
73
#include "cmStateDirectory.h"
74
#include "cmStringAlgorithms.h"
75
#include "cmSystemTools.h"
76
#include "cmTarget.h"
77
#include "cmTargetLinkLibraryType.h"
78
#include "cmUVProcessChain.h"
79
#include "cmUtils.hxx"
80
#include "cmVersionConfig.h"
81
#include "cmWorkingDirectory.h"
82
83
#if !defined(CMAKE_BOOTSTRAP)
84
#  include <unordered_map>
85
86
#  include <cm3p/curl/curl.h>
87
#  include <cm3p/json/writer.h>
88
89
#  include "cmConfigureLog.h"
90
#  include "cmFileAPI.h"
91
#  include "cmGraphVizWriter.h"
92
#  include "cmInstrumentation.h"
93
#  include "cmInstrumentationQuery.h"
94
#  include "cmVariableWatch.h"
95
#endif
96
97
#if defined(__MINGW32__) && defined(CMAKE_BOOTSTRAP)
98
#  define CMAKE_BOOT_MINGW
99
#endif
100
101
// include the generator
102
#if defined(_WIN32) && !defined(__CYGWIN__)
103
#  if !defined(CMAKE_BOOT_MINGW)
104
#    include <cmext/memory>
105
106
#    include "cmGlobalBorlandMakefileGenerator.h"
107
#    include "cmGlobalFastbuildGenerator.h"
108
#    include "cmGlobalJOMMakefileGenerator.h"
109
#    include "cmGlobalNMakeMakefileGenerator.h"
110
#    include "cmGlobalVisualStudio14Generator.h"
111
#    include "cmGlobalVisualStudioVersionedGenerator.h"
112
#    include "cmVSSetupHelper.h"
113
114
#    define CMAKE_HAVE_VS_GENERATORS
115
#  endif
116
#  include "cmGlobalMSYSMakefileGenerator.h"
117
#  include "cmGlobalMinGWMakefileGenerator.h"
118
#else
119
#endif
120
#if defined(CMAKE_USE_WMAKE)
121
#  include "cmGlobalWatcomWMakeGenerator.h"
122
#endif
123
#if !defined(CMAKE_BOOTSTRAP)
124
#  include "cmGlobalNinjaGenerator.h"
125
#  include "cmGlobalUnixMakefileGenerator3.h"
126
#elif defined(CMAKE_BOOTSTRAP_MAKEFILES)
127
#  include "cmGlobalUnixMakefileGenerator3.h"
128
#elif defined(CMAKE_BOOTSTRAP_NINJA)
129
#  include "cmGlobalNinjaGenerator.h"
130
#endif
131
#include "cmGlobalFastbuildGenerator.h"
132
133
#if !defined(CMAKE_BOOTSTRAP)
134
#  include "cmExtraCodeBlocksGenerator.h"
135
#  include "cmExtraCodeLiteGenerator.h"
136
#  include "cmExtraEclipseCDT4Generator.h"
137
#  include "cmExtraKateGenerator.h"
138
#  include "cmExtraSublimeTextGenerator.h"
139
140
// NOTE: the __linux__ macro is predefined on Android host too, but
141
// main CMakeLists.txt filters out this generator by host name.
142
#  if (defined(__linux__) && !defined(__ANDROID__)) || defined(_WIN32)
143
#    include "cmGlobalGhsMultiGenerator.h"
144
#  endif
145
#endif
146
147
#if defined(__APPLE__)
148
#  if !defined(CMAKE_BOOTSTRAP)
149
#    include "cmGlobalXCodeGenerator.h"
150
151
#    define CMAKE_USE_XCODE 1
152
#  endif
153
#  include <sys/resource.h>
154
#  include <sys/time.h>
155
#endif
156
157
namespace {
158
159
#if !defined(CMAKE_BOOTSTRAP)
160
using JsonValueMapType = std::unordered_map<std::string, Json::Value>;
161
#endif
162
163
35
auto IgnoreAndTrueLambda = [](std::string const&, cmake*) -> bool {
164
35
  return true;
165
35
};
166
167
using CommandArgument =
168
  cmCommandLineArgument<bool(std::string const& value, cmake* state)>;
169
170
#ifndef CMAKE_BOOTSTRAP
171
void cmWarnUnusedCliWarning(std::string const& variable, int /*unused*/,
172
                            void* ctx, char const* /*unused*/,
173
                            cmMakefile const* /*unused*/)
174
0
{
175
0
  cmake* cm = reinterpret_cast<cmake*>(ctx);
176
0
  cm->MarkCliAsUsed(variable);
177
0
}
178
#endif
179
180
void warnDeprecated(cm::string_view oldOption, cm::string_view newOption)
181
0
{
182
0
  std::cerr << "The "_s << oldOption << " option is deprecated.  Use "_s
183
0
            << newOption << " instead.\n"_s;
184
0
}
185
186
std::string normalizeCliWarningName(cm::string_view cliName)
187
0
{
188
0
  std::string out = cmStrCat("CMD_"_s, cmSystemTools::UpperCase(cliName));
189
0
  std::replace(out.begin(), out.end(), '-', '_');
190
0
  return out;
191
0
}
192
193
bool cmakeCheckStampFile(std::string const& stampName)
194
0
{
195
  // The stamp file does not exist.  Use the stamp dependencies to
196
  // determine whether it is really out of date.  This works in
197
  // conjunction with cmLocalVisualStudio7Generator to avoid
198
  // repeatedly re-running CMake when the user rebuilds the entire
199
  // solution.
200
0
  std::string stampDepends = cmStrCat(stampName, ".depend");
201
#if defined(_WIN32) || defined(__CYGWIN__)
202
  cmsys::ifstream fin(stampDepends.c_str(), std::ios::in | std::ios::binary);
203
#else
204
0
  cmsys::ifstream fin(stampDepends.c_str());
205
0
#endif
206
0
  if (!fin) {
207
    // The stamp dependencies file cannot be read.  Just assume the
208
    // build system is really out of date.
209
0
    std::cout << "CMake is re-running because " << stampName
210
0
              << " dependency file is missing.\n";
211
0
    return false;
212
0
  }
213
214
  // Compare the stamp dependencies against the dependency file itself.
215
0
  {
216
0
    cmFileTimeCache ftc;
217
0
    std::string dep;
218
0
    while (cmSystemTools::GetLineFromStream(fin, dep)) {
219
0
      int result;
220
0
      if (!dep.empty() && dep[0] != '#' &&
221
0
          (!ftc.Compare(stampDepends, dep, &result) || result < 0)) {
222
        // The stamp depends file is older than this dependency.  The
223
        // build system is really out of date.
224
        /* clang-format off */
225
0
        std::cout << "CMake is re-running because " << stampName
226
0
                  << " is out-of-date.\n"
227
0
                     "  the file '" << dep << "'\n"
228
0
                     "  is newer than '" << stampDepends << "'\n"
229
0
                     "  result='" << result << "'\n";
230
        /* clang-format on */
231
0
        return false;
232
0
      }
233
0
    }
234
0
  }
235
236
  // The build system is up to date.  The stamp file has been removed
237
  // by the VS IDE due to a "rebuild" request.  Restore it atomically.
238
0
  std::ostringstream stampTempStream;
239
0
  stampTempStream << stampName << ".tmp" << cmSystemTools::RandomNumber();
240
0
  std::string stampTemp = stampTempStream.str();
241
0
  {
242
    // TODO: Teach cmGeneratedFileStream to use a random temp file (with
243
    // multiple tries in unlikely case of conflict) and use that here.
244
0
    cmsys::ofstream stamp(stampTemp.c_str());
245
0
    stamp << "# CMake generation timestamp file for this directory.\n";
246
0
  }
247
0
  std::string err;
248
0
  if (cmSystemTools::RenameFile(stampTemp, stampName,
249
0
                                cmSystemTools::Replace::Yes, &err) ==
250
0
      cmSystemTools::RenameResult::Success) {
251
    // CMake does not need to re-run because the stamp file is up-to-date.
252
0
    return true;
253
0
  }
254
0
  cmSystemTools::RemoveFile(stampTemp);
255
0
  cmSystemTools::Error(
256
0
    cmStrCat("Cannot restore timestamp \"", stampName, "\": ", err));
257
0
  return false;
258
0
}
259
260
bool cmakeCheckStampList(std::string const& stampList)
261
0
{
262
  // If the stamp list does not exist CMake must rerun to generate it.
263
0
  if (!cmSystemTools::FileExists(stampList)) {
264
0
    std::cout << "CMake is re-running because generate.stamp.list "
265
0
                 "is missing.\n";
266
0
    return false;
267
0
  }
268
0
  cmsys::ifstream fin(stampList.c_str());
269
0
  if (!fin) {
270
0
    std::cout << "CMake is re-running because generate.stamp.list "
271
0
                 "could not be read.\n";
272
0
    return false;
273
0
  }
274
275
  // Check each stamp.
276
0
  std::string stampName;
277
0
  while (cmSystemTools::GetLineFromStream(fin, stampName)) {
278
0
    if (!cmakeCheckStampFile(stampName)) {
279
0
      return false;
280
0
    }
281
0
  }
282
0
  return true;
283
0
}
284
285
bool isDiagnosticSet(cmStateSnapshot const& state,
286
                     cmDiagnosticCategory category)
287
0
{
288
0
  constexpr cmDiagnosticAction unset = cmDiagnostics::Undefined;
289
0
  return (state.GetDiagnostic(category, unset) == unset);
290
0
}
291
292
} // namespace
293
294
cmDocumentationEntry cmake::CMAKE_STANDARD_OPTIONS_TABLE[15] = {
295
  { "-S <path-to-source>", "Explicitly specify a source directory." },
296
  { "-B <path-to-build>", "Explicitly specify a build directory." },
297
  { "-C <initial-cache>", "Pre-load a script to populate the cache." },
298
  { "-D <var>[:<type>]=<value>", "Create or update a cmake cache entry." },
299
  { "-U <globbing_expr>", "Remove matching entries from CMake cache." },
300
  { "-G <generator-name>", "Specify a build system generator." },
301
  { "-T <toolset-name>", "Specify toolset name if supported by generator." },
302
  { "-A <platform-name>", "Specify platform name if supported by generator." },
303
  { "--toolchain <file>", "Specify toolchain file [CMAKE_TOOLCHAIN_FILE]." },
304
  { "--install-prefix <directory>",
305
    "Specify install directory [CMAKE_INSTALL_PREFIX]." },
306
  { "--project-file <project-file-name>",
307
    "Specify an alternate project file name." },
308
  { "-W<category>", "Enable the specified category of warnings." },
309
  { "-Wno-<category>", "Suppress the specified category of warnings." },
310
  { "-Werror=<category>", "Make the specified category of warnings errors." },
311
  { "-Wno-error=<category>",
312
    "Make the specified category of warnings not errors." },
313
};
314
315
cmake::cmake(cmState::Role role, cmState::TryCompile isTryCompile)
316
35
  : CMakeWorkingDirectory(cmSystemTools::GetLogicalWorkingDirectory())
317
35
  , FileTimeCache(cm::make_unique<cmFileTimeCache>())
318
#ifndef CMAKE_BOOTSTRAP
319
35
  , VariableWatch(cm::make_unique<cmVariableWatch>())
320
#endif
321
35
  , State(cm::make_unique<cmState>(role, isTryCompile))
322
35
  , Messenger(cm::make_unique<cmMessenger>())
323
35
{
324
35
  this->TraceFile.close();
325
35
  this->CurrentSnapshot = this->State->CreateBaseSnapshot();
326
327
#ifdef __APPLE__
328
  struct rlimit rlp;
329
  if (!getrlimit(RLIMIT_STACK, &rlp)) {
330
    if (rlp.rlim_cur != rlp.rlim_max) {
331
      rlp.rlim_cur = rlp.rlim_max;
332
      setrlimit(RLIMIT_STACK, &rlp);
333
    }
334
  }
335
#endif
336
337
35
  this->AddDefaultGenerators();
338
35
  this->AddDefaultExtraGenerators();
339
35
  if (role == cmState::Role::Project || role == cmState::Role::FindPackage ||
340
35
      role == cmState::Role::Script || role == cmState::Role::CTest ||
341
35
      role == cmState::Role::CPack) {
342
35
    this->AddScriptingCommands();
343
35
  }
344
35
  if (role == cmState::Role::Project || role == cmState::Role::FindPackage) {
345
0
    this->AddProjectCommands();
346
0
  }
347
348
35
  if (role == cmState::Role::Project || role == cmState::Role::Help) {
349
0
    this->LoadEnvironmentPresets();
350
0
  }
351
352
  // Make sure we can capture the build tool output.
353
35
  cmSystemTools::EnableVSConsoleOutput();
354
355
  // Set up a list of source and header extensions.
356
  // These are used to find files when the extension is not given.
357
35
  {
358
35
    auto setupExts = [](FileExtensions& exts,
359
210
                        std::initializer_list<cm::string_view> extList) {
360
      // Fill ordered vector
361
210
      exts.ordered.reserve(extList.size());
362
1.19k
      for (cm::string_view ext : extList) {
363
1.19k
        exts.ordered.emplace_back(ext);
364
1.19k
      }
365
      // Fill unordered set
366
210
      exts.unordered.insert(exts.ordered.begin(), exts.ordered.end());
367
210
    };
368
369
    // The "c" extension MUST precede the "C" extension.
370
35
    setupExts(this->CLikeSourceFileExtensions,
371
35
              { "c", "C", "c++", "cc", "cpp", "cxx", "cu", "mpp", "m", "M",
372
35
                "mm", "ixx", "cppm", "ccm", "cxxm", "c++m" });
373
35
    setupExts(this->HeaderFileExtensions,
374
35
              { "h", "hh", "h++", "hm", "hpp", "hxx", "in", "txx" });
375
35
    setupExts(this->CudaFileExtensions, { "cu" });
376
35
    setupExts(this->FortranFileExtensions,
377
35
              { "f", "F", "for", "f77", "f90", "f95", "f03" });
378
35
    setupExts(this->HipFileExtensions, { "hip" });
379
35
    setupExts(this->ISPCFileExtensions, { "ispc" });
380
35
  }
381
35
}
382
383
35
cmake::~cmake() = default;
384
385
#if !defined(CMAKE_BOOTSTRAP)
386
Json::Value cmake::ReportVersionJson() const
387
0
{
388
0
  Json::Value version = Json::objectValue;
389
0
  version["string"] = CMake_VERSION;
390
0
  version["major"] = CMake_VERSION_MAJOR;
391
0
  version["minor"] = CMake_VERSION_MINOR;
392
0
  version["suffix"] = CMake_VERSION_SUFFIX;
393
0
  version["isDirty"] = (CMake_VERSION_IS_DIRTY == 1);
394
0
  version["patch"] = CMake_VERSION_PATCH;
395
0
  return version;
396
0
}
397
398
Json::Value cmake::ReportCapabilitiesJson() const
399
0
{
400
0
  Json::Value obj = Json::objectValue;
401
402
  // Version information:
403
0
  obj["version"] = this->ReportVersionJson();
404
405
  // Generators:
406
0
  std::vector<cmake::GeneratorInfo> generatorInfoList;
407
0
  this->GetRegisteredGenerators(generatorInfoList);
408
409
0
  auto* curlVersion = curl_version_info(CURLVERSION_FIRST);
410
411
0
  JsonValueMapType generatorMap;
412
0
  for (cmake::GeneratorInfo const& gi : generatorInfoList) {
413
0
    if (gi.isAlias) { // skip aliases, they are there for compatibility reasons
414
                      // only
415
0
      continue;
416
0
    }
417
418
0
    if (gi.extraName.empty()) {
419
0
      Json::Value gen = Json::objectValue;
420
0
      gen["name"] = gi.name;
421
0
      gen["toolsetSupport"] = gi.supportsToolset;
422
0
      gen["platformSupport"] = gi.supportsPlatform;
423
0
      if (!gi.supportedPlatforms.empty()) {
424
0
        Json::Value supportedPlatforms = Json::arrayValue;
425
0
        for (std::string const& platform : gi.supportedPlatforms) {
426
0
          supportedPlatforms.append(platform);
427
0
        }
428
0
        gen["supportedPlatforms"] = std::move(supportedPlatforms);
429
0
      }
430
0
      gen["extraGenerators"] = Json::arrayValue;
431
0
      generatorMap[gi.name] = gen;
432
0
    } else {
433
0
      Json::Value& gen = generatorMap[gi.baseName];
434
0
      gen["extraGenerators"].append(gi.extraName);
435
0
    }
436
0
  }
437
438
0
  Json::Value generators = Json::arrayValue;
439
0
  for (auto const& i : generatorMap) {
440
0
    generators.append(i.second);
441
0
  }
442
0
  obj["generators"] = generators;
443
0
  obj["fileApi"] = cmFileAPI::ReportCapabilities();
444
0
  obj["serverMode"] = false;
445
0
  obj["tls"] = static_cast<bool>(curlVersion->features & CURL_VERSION_SSL);
446
0
#  ifdef CMake_ENABLE_DEBUGGER
447
0
  obj["debugger"] = true;
448
#  else
449
  obj["debugger"] = false;
450
#  endif
451
452
0
  return obj;
453
0
}
454
#endif
455
456
std::string cmake::ReportCapabilities() const
457
0
{
458
0
  std::string result;
459
0
#if !defined(CMAKE_BOOTSTRAP)
460
0
  Json::FastWriter writer;
461
0
  result = writer.write(this->ReportCapabilitiesJson());
462
#else
463
  result = "Not supported";
464
#endif
465
0
  return result;
466
0
}
467
468
bool cmake::RoleSupportsExitCode() const
469
0
{
470
0
  cmState::Role const role = this->State->GetRole();
471
0
  return role == cmState::Role::Script || role == cmState::Role::CTest;
472
0
}
473
474
cmake::CommandFailureAction cmake::GetCommandFailureAction() const
475
0
{
476
0
  switch (this->State->GetRole()) {
477
0
    case cmState::Role::Project:
478
0
    case cmState::Role::CTest:
479
0
      return CommandFailureAction::EXIT_CODE;
480
0
    default:
481
0
      return CommandFailureAction::FATAL_ERROR;
482
0
  }
483
0
}
484
485
void cmake::CleanupCommandsAndMacros()
486
0
{
487
0
  this->CurrentSnapshot = this->State->Reset(this->CurrentSnapshot);
488
0
  this->State->RemoveUserDefinedCommands();
489
0
  this->CurrentSnapshot.SetDefaultDefinitions();
490
  // FIXME: InstalledFiles probably belongs in the global generator.
491
0
  this->InstalledFiles.clear();
492
0
}
493
494
#ifndef CMAKE_BOOTSTRAP
495
void cmake::SetDiagnosticsFromPreset(
496
  std::map<cmDiagnosticCategory, bool> const& warnings,
497
  std::map<cmDiagnosticCategory, bool> const& errors)
498
0
{
499
0
  for (unsigned i = 1; i < cmDiagnostics::CategoryCount; ++i) {
500
0
    auto const category = static_cast<cmDiagnosticCategory>(i);
501
502
0
    auto const wi = warnings.find(category);
503
0
    if (wi != warnings.end()) {
504
0
      if (wi->second) {
505
0
        this->CurrentSnapshot.PromoteDiagnostic( // clang-format: break
506
0
          category, cmDiagnostics::Warn, true);
507
0
      } else {
508
0
        this->CurrentSnapshot.DemoteDiagnostic( // clang-format: break
509
0
          category, cmDiagnostics::Ignore, true);
510
0
      }
511
0
    }
512
513
0
    auto const ei = errors.find(category);
514
0
    if (ei != errors.end()) {
515
0
      if (ei->second) {
516
0
        this->CurrentSnapshot.PromoteDiagnostic( // clang-format: break
517
0
          category, cmDiagnostics::SendError, true);
518
0
      } else {
519
0
        this->CurrentSnapshot.DemoteDiagnostic( // clang-format: break
520
0
          category, cmDiagnostics::Warn, true);
521
0
      }
522
0
    }
523
0
  }
524
0
}
525
526
void cmake::ProcessPresetVariables()
527
1
{
528
1
  for (auto const& var : this->UnprocessedPresetVariables) {
529
0
    if (!var.second) {
530
0
      continue;
531
0
    }
532
0
    cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
533
0
    if (!var.second->Type.empty()) {
534
0
      type = cmState::StringToCacheEntryType(var.second->Type);
535
0
    }
536
0
    this->ProcessCacheArg(var.first, var.second->Value, type);
537
0
  }
538
1
}
539
540
void cmake::PrintPresetVariables()
541
0
{
542
0
  bool first = true;
543
0
  for (auto const& var : this->UnprocessedPresetVariables) {
544
0
    if (!var.second) {
545
0
      continue;
546
0
    }
547
0
    cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
548
0
    if (!var.second->Type.empty()) {
549
0
      type = cmState::StringToCacheEntryType(var.second->Type);
550
0
    }
551
0
    if (first) {
552
0
      std::cout << "Preset CMake variables:\n\n";
553
0
      first = false;
554
0
    }
555
0
    std::cout << "  " << var.first;
556
0
    if (type != cmStateEnums::UNINITIALIZED) {
557
0
      std::cout << ':' << cmState::CacheEntryTypeToString(type);
558
0
    }
559
0
    std::cout << "=\"" << var.second->Value << "\"\n";
560
0
  }
561
0
  if (!first) {
562
0
    std::cout << '\n';
563
0
  }
564
0
  this->UnprocessedPresetVariables.clear();
565
0
}
566
567
void cmake::ProcessPresetEnvironment()
568
1
{
569
1
  for (auto const& var : this->UnprocessedPresetEnvironment) {
570
0
    if (var.second) {
571
0
      cmSystemTools::PutEnv(cmStrCat(var.first, '=', *var.second));
572
0
    }
573
0
  }
574
1
}
575
576
void cmake::PrintPresetEnvironment()
577
0
{
578
0
  bool first = true;
579
0
  for (auto const& var : this->UnprocessedPresetEnvironment) {
580
0
    if (!var.second) {
581
0
      continue;
582
0
    }
583
0
    if (first) {
584
0
      std::cout << "Preset environment variables:\n\n";
585
0
      first = false;
586
0
    }
587
0
    std::cout << "  " << var.first << "=\"" << *var.second << "\"\n";
588
0
  }
589
0
  if (!first) {
590
0
    std::cout << '\n';
591
0
  }
592
0
  this->UnprocessedPresetEnvironment.clear();
593
0
}
594
#endif
595
596
// Parse the args
597
bool cmake::SetCacheArgs(std::vector<std::string> const& args)
598
1
{
599
1
  static std::string const kCMAKE_POLICY_VERSION_MINIMUM =
600
1
    "CMAKE_POLICY_VERSION_MINIMUM";
601
1
  if (!this->State->GetInitializedCacheValue(kCMAKE_POLICY_VERSION_MINIMUM)) {
602
1
    cm::optional<std::string> policyVersion =
603
1
      cmSystemTools::GetEnvVar(kCMAKE_POLICY_VERSION_MINIMUM);
604
1
    if (policyVersion && !policyVersion->empty()) {
605
0
      this->AddCacheEntry(
606
0
        kCMAKE_POLICY_VERSION_MINIMUM, *policyVersion,
607
0
        "Override policy version for cmake_minimum_required calls.",
608
0
        cmStateEnums::STRING);
609
0
      this->State->SetCacheEntryProperty(kCMAKE_POLICY_VERSION_MINIMUM,
610
0
                                         "ADVANCED", "1");
611
0
    }
612
1
  }
613
614
1
  auto DefineLambda = [](std::string const& entry, cmake* state) -> bool {
615
0
    std::string var;
616
0
    std::string value;
617
0
    cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
618
0
    if (cmState::ParseCacheEntry(entry, var, value, type)) {
619
0
#ifndef CMAKE_BOOTSTRAP
620
0
      state->UnprocessedPresetVariables.erase(var);
621
0
#endif
622
0
      state->ProcessCacheArg(var, value, type);
623
0
    } else {
624
0
      cmSystemTools::Error(cmStrCat("Parse error in command line argument: ",
625
0
                                    entry, "\n Should be: VAR:type=value\n"));
626
0
      return false;
627
0
    }
628
0
    return true;
629
0
  };
630
631
1
  auto WarningLambda = [](cm::string_view option, cmake* state) -> bool {
632
0
    bool foundNo = false;
633
0
    bool foundError = false;
634
635
0
    cm::string_view cname = option;
636
0
    if (cmHasLiteralPrefix(cname, "no-")) {
637
0
      foundNo = true;
638
0
      cname.remove_prefix(3);
639
0
    }
640
641
0
    if (cmHasLiteralPrefix(cname, "error=")) {
642
0
      foundError = true;
643
0
      cname.remove_prefix(6);
644
0
    }
645
646
0
    if (cname.empty()) {
647
0
      cmSystemTools::Error("No warning name provided.");
648
0
      return false;
649
0
    }
650
651
0
    cm::optional<cmDiagnosticCategory> category;
652
0
    if (cname == "dev"_s) {
653
0
      warnDeprecated(
654
0
        option,
655
0
        cmStrCat("-W"_s, option.substr(0, option.size() - 3), "author"_s));
656
0
      category = cmDiagnostics::CMD_AUTHOR;
657
0
    } else {
658
0
      category =
659
0
        cmDiagnostics::GetDiagnosticCategory(normalizeCliWarningName(cname));
660
0
      if (!category) {
661
0
        cmSystemTools::Error(
662
0
          cmStrCat("The warning category \""_s, cname, "\" is not known."));
663
0
        return false;
664
0
      }
665
0
    }
666
667
0
    if (foundNo) {
668
0
      state->CurrentSnapshot.DemoteDiagnostic(
669
0
        *category, foundError ? cmDiagnostics::Warn : cmDiagnostics::Ignore,
670
0
        true);
671
0
    } else {
672
0
      state->CurrentSnapshot.PromoteDiagnostic(
673
0
        *category, foundError ? cmDiagnostics::SendError : cmDiagnostics::Warn,
674
0
        true);
675
0
    }
676
0
    return true;
677
0
  };
678
679
1
  auto UnSetLambda = [](std::string const& entryPattern,
680
1
                        cmake* state) -> bool {
681
0
    cmsys::RegularExpression regex(
682
0
      cmsys::Glob::PatternToRegex(entryPattern, true, true));
683
    // go through all cache entries and collect the vars which will be
684
    // removed
685
0
    std::vector<std::string> entriesToDelete;
686
0
    std::vector<std::string> cacheKeys = state->State->GetCacheEntryKeys();
687
0
    for (std::string const& ck : cacheKeys) {
688
0
      cmStateEnums::CacheEntryType t = state->State->GetCacheEntryType(ck);
689
0
      if (t != cmStateEnums::STATIC) {
690
0
        if (regex.find(ck)) {
691
0
          entriesToDelete.push_back(ck);
692
0
        }
693
0
      }
694
0
    }
695
696
    // now remove them from the cache
697
0
    for (std::string const& currentEntry : entriesToDelete) {
698
0
#ifndef CMAKE_BOOTSTRAP
699
0
      state->UnprocessedPresetVariables.erase(currentEntry);
700
0
#endif
701
0
      state->State->RemoveCacheEntry(currentEntry);
702
0
    }
703
0
    return true;
704
0
  };
705
706
1
  auto ScriptLambda = [&](std::string const& path, cmake* state) -> bool {
707
1
    assert(this->State->GetRole() == cmState::Role::Script);
708
1
#ifdef CMake_ENABLE_DEBUGGER
709
    // Script mode doesn't hit the usual code path in cmake::Run() that starts
710
    // the debugger, so start it manually here instead.
711
1
    if (!this->StartDebuggerIfEnabled()) {
712
0
      return false;
713
0
    }
714
1
#endif
715
    // Register fake project commands that hint misuse in script mode.
716
1
    GetProjectCommandsInScriptMode(state->GetState());
717
    // Documented behavior of CMAKE{,_CURRENT}_{SOURCE,BINARY}_DIR is to be
718
    // set to $PWD for -P mode.
719
1
    state->SetHomeDirectory(cmSystemTools::GetLogicalWorkingDirectory());
720
1
    state->SetHomeOutputDirectory(cmSystemTools::GetLogicalWorkingDirectory());
721
1
    state->ReadListFile(args, cmSystemTools::ToNormalizedPathOnDisk(path));
722
1
    return true;
723
1
  };
724
725
1
  auto PrefixLambda = [&](std::string const& path, cmake* state) -> bool {
726
0
    std::string const var = "CMAKE_INSTALL_PREFIX";
727
0
    cmStateEnums::CacheEntryType type = cmStateEnums::PATH;
728
0
    cmCMakePath absolutePath(path);
729
0
    if (absolutePath.IsAbsolute()) {
730
0
#ifndef CMAKE_BOOTSTRAP
731
0
      state->UnprocessedPresetVariables.erase(var);
732
0
#endif
733
0
      state->ProcessCacheArg(var, path, type);
734
0
      return true;
735
0
    }
736
0
    cmSystemTools::Error("Absolute paths are required for --install-prefix");
737
0
    return false;
738
0
  };
739
740
1
  auto ToolchainLambda = [&](std::string const& path, cmake* state) -> bool {
741
0
    std::string const var = "CMAKE_TOOLCHAIN_FILE";
742
0
    cmStateEnums::CacheEntryType type = cmStateEnums::FILEPATH;
743
0
#ifndef CMAKE_BOOTSTRAP
744
0
    state->UnprocessedPresetVariables.erase(var);
745
0
#endif
746
0
    state->ProcessCacheArg(var, path, type);
747
0
    return true;
748
0
  };
749
750
1
  std::vector<CommandArgument> arguments = {
751
1
    CommandArgument{ "-D", "-D must be followed with VAR=VALUE.",
752
1
                     CommandArgument::Values::One,
753
1
                     CommandArgument::RequiresSeparator::No, DefineLambda },
754
1
    CommandArgument{ "-W", "-W must be followed with [no-]<name>.",
755
1
                     CommandArgument::Values::One,
756
1
                     CommandArgument::RequiresSeparator::No, WarningLambda },
757
1
    CommandArgument{ "-U", "-U must be followed with VAR.",
758
1
                     CommandArgument::Values::One,
759
1
                     CommandArgument::RequiresSeparator::No, UnSetLambda },
760
1
    CommandArgument{
761
1
      "-C", "-C must be followed by a file name.",
762
1
      CommandArgument::Values::One, CommandArgument::RequiresSeparator::No,
763
1
      [&](std::string const& value, cmake* state) -> bool {
764
0
        if (value.empty()) {
765
0
          cmSystemTools::Error("No file name specified for -C");
766
0
          return false;
767
0
        }
768
0
        state->SetInInitialCache(true);
769
0
        cmSystemTools::Stdout(
770
0
          cmStrCat("loading initial cache file ", value, '\n'));
771
        // Resolve script path specified on command line
772
        // relative to $PWD.
773
0
        auto path = cmSystemTools::ToNormalizedPathOnDisk(value);
774
0
        state->InitializeFileAPI();
775
0
        state->ReadListFile(args, path);
776
0
        state->SetInInitialCache(false);
777
0
        return true;
778
0
      } },
779
780
1
    CommandArgument{ "-P", "-P must be followed by a file name.",
781
1
                     CommandArgument::Values::One,
782
1
                     CommandArgument::RequiresSeparator::No, ScriptLambda },
783
1
    CommandArgument{ "--toolchain", "No file specified for --toolchain",
784
1
                     CommandArgument::Values::One, ToolchainLambda },
785
1
    CommandArgument{ "--install-prefix",
786
1
                     "No install directory specified for --install-prefix",
787
1
                     CommandArgument::Values::One, PrefixLambda },
788
1
    CommandArgument{ "--find-package", CommandArgument::Values::Zero,
789
1
                     IgnoreAndTrueLambda },
790
1
  };
791
2
  for (decltype(args.size()) i = 1; i < args.size(); ++i) {
792
1
    std::string const& arg = args[i];
793
794
1
    if (arg == "--" && this->State->GetRole() == cmState::Role::Script) {
795
      // Stop processing CMake args and avoid possible errors
796
      // when arbitrary args are given to CMake script.
797
0
      break;
798
0
    }
799
8
    for (auto const& m : arguments) {
800
8
      if (m.matches(arg)) {
801
1
        bool const parsedCorrectly = m.parse(arg, i, args, this);
802
1
        if (!parsedCorrectly) {
803
0
          return false;
804
0
        }
805
1
      }
806
8
    }
807
1
  }
808
809
1
  if (this->State->GetRole() == cmState::Role::FindPackage) {
810
0
    return this->FindPackage(args);
811
0
  }
812
813
1
  return true;
814
1
}
815
816
void cmake::ProcessCacheArg(std::string const& var, std::string const& value,
817
                            cmStateEnums::CacheEntryType type)
818
0
{
819
0
  cmDiagnosticAction const warnUnusedCli =
820
0
    this->CurrentSnapshot.GetDiagnostic(cmDiagnostics::CMD_UNUSED_CLI);
821
822
  // The value is transformed if it is a filepath for example, so
823
  // we can't compare whether the value is already in the cache until
824
  // after we call AddCacheEntry.
825
0
  bool haveValue = false;
826
0
  std::string cachedValue;
827
0
  if (warnUnusedCli != cmDiagnostics::Ignore) {
828
0
    if (cmValue v = this->State->GetInitializedCacheValue(var)) {
829
0
      haveValue = true;
830
0
      cachedValue = *v;
831
0
    }
832
0
  }
833
834
0
  this->AddCacheEntry(
835
0
    var, value, "No help, variable specified on the command line.", type);
836
837
0
  if (warnUnusedCli != cmDiagnostics::Ignore) {
838
0
    if (!haveValue ||
839
0
        cachedValue != *this->State->GetInitializedCacheValue(var)) {
840
0
      this->WatchUnusedCli(var);
841
0
    }
842
0
  }
843
0
}
844
845
void cmake::ReadListFile(std::vector<std::string> const& args,
846
                         std::string const& path)
847
1
{
848
  // if a generator was not yet created, temporarily create one
849
1
  cmGlobalGenerator* gg = this->GetGlobalGenerator();
850
851
  // if a generator was not specified use a generic one
852
1
  std::unique_ptr<cmGlobalGenerator> gen;
853
1
  if (!gg) {
854
1
    gen = cm::make_unique<cmGlobalGenerator>(this);
855
1
    gg = gen.get();
856
1
  }
857
858
  // read in the list file to fill the cache
859
1
  if (!path.empty()) {
860
1
    this->CurrentSnapshot = this->State->Reset(this->CurrentSnapshot);
861
1
    cmStateSnapshot snapshot = this->GetCurrentSnapshot();
862
1
    snapshot.GetDirectory().SetCurrentBinary(this->GetHomeOutputDirectory());
863
1
    snapshot.GetDirectory().SetCurrentSource(this->GetHomeDirectory());
864
1
    snapshot.SetDefaultDefinitions();
865
1
    cmMakefile mf(gg, snapshot);
866
1
    if (this->State->GetRole() == cmState::Role::Script) {
867
1
      mf.SetScriptModeFile(path);
868
1
      mf.SetArgcArgv(args);
869
1
    }
870
1
    if (!cmSystemTools::FileExists(path, true)) {
871
0
      cmSystemTools::Error("Not a file: " + path);
872
0
    }
873
1
    if (!mf.ReadListFile(path)) {
874
1
      cmSystemTools::Error("Error processing file: " + path);
875
1
    }
876
1
  }
877
1
}
878
879
bool cmake::FindPackage(std::vector<std::string> const& args)
880
0
{
881
0
  this->SetHomeDirectory(cmSystemTools::GetLogicalWorkingDirectory());
882
0
  this->SetHomeOutputDirectory(cmSystemTools::GetLogicalWorkingDirectory());
883
884
0
  this->SetGlobalGenerator(cm::make_unique<cmGlobalGenerator>(this));
885
886
0
  cmStateSnapshot snapshot = this->GetCurrentSnapshot();
887
0
  snapshot.GetDirectory().SetCurrentBinary(
888
0
    cmSystemTools::GetLogicalWorkingDirectory());
889
0
  snapshot.GetDirectory().SetCurrentSource(
890
0
    cmSystemTools::GetLogicalWorkingDirectory());
891
  // read in the list file to fill the cache
892
0
  snapshot.SetDefaultDefinitions();
893
0
  auto mfu = cm::make_unique<cmMakefile>(this->GetGlobalGenerator(), snapshot);
894
0
  cmMakefile* mf = mfu.get();
895
0
  this->GlobalGenerator->AddMakefile(std::move(mfu));
896
897
0
  mf->SetArgcArgv(args);
898
899
0
  std::string systemFile = mf->GetModulesFile("CMakeFindPackageMode.cmake");
900
0
  mf->ReadListFile(systemFile);
901
902
0
  std::string language = mf->GetSafeDefinition("LANGUAGE");
903
0
  std::string mode = mf->GetSafeDefinition("MODE");
904
0
  std::string packageName = mf->GetSafeDefinition("NAME");
905
0
  bool packageFound = mf->IsOn("PACKAGE_FOUND");
906
0
  bool quiet = mf->IsOn("PACKAGE_QUIET");
907
908
0
  if (!packageFound) {
909
0
    if (!quiet) {
910
0
      printf("%s not found.\n", packageName.c_str());
911
0
    }
912
0
  } else if (mode == "EXIST"_s) {
913
0
    if (!quiet) {
914
0
      printf("%s found.\n", packageName.c_str());
915
0
    }
916
0
  } else if (mode == "COMPILE"_s) {
917
0
    std::string includes = mf->GetSafeDefinition("PACKAGE_INCLUDE_DIRS");
918
0
    cmList includeDirs{ includes };
919
920
0
    this->GlobalGenerator->CreateGenerationObjects();
921
0
    auto const& lg = this->GlobalGenerator->LocalGenerators[0];
922
0
    std::string includeFlags =
923
0
      lg->GetIncludeFlags(includeDirs, nullptr, language, std::string());
924
925
0
    std::string definitions = mf->GetSafeDefinition("PACKAGE_DEFINITIONS");
926
0
    printf("%s %s\n", includeFlags.c_str(), definitions.c_str());
927
0
  } else if (mode == "LINK"_s) {
928
0
    char const* targetName = "dummy";
929
0
    std::vector<std::string> srcs;
930
0
    cmTarget* tgt = mf->AddExecutable(targetName, srcs, true);
931
0
    tgt->SetProperty("LINKER_LANGUAGE", language);
932
933
0
    std::string libs = mf->GetSafeDefinition("PACKAGE_LIBRARIES");
934
0
    cmList libList{ libs };
935
0
    for (std::string const& lib : libList) {
936
0
      tgt->AddLinkLibrary(*mf, lib, GENERAL_LibraryType);
937
0
    }
938
939
0
    std::string buildType = mf->GetSafeDefinition("CMAKE_BUILD_TYPE");
940
0
    buildType = cmSystemTools::UpperCase(buildType);
941
942
0
    std::string linkLibs;
943
0
    std::string frameworkPath;
944
0
    std::string linkPath;
945
0
    std::string flags;
946
0
    std::string linkFlags;
947
0
    this->GlobalGenerator->CreateGenerationObjects();
948
0
    cmGeneratorTarget* gtgt =
949
0
      this->GlobalGenerator->FindGeneratorTarget(tgt->GetName());
950
0
    cmLocalGenerator* lg = gtgt->GetLocalGenerator();
951
0
    cmLinkLineComputer linkLineComputer(lg,
952
0
                                        lg->GetStateSnapshot().GetDirectory());
953
0
    lg->GetTargetFlags(&linkLineComputer, buildType, linkLibs, flags,
954
0
                       linkFlags, frameworkPath, linkPath, gtgt);
955
0
    linkLibs = frameworkPath + linkPath + linkLibs;
956
957
0
    printf("%s\n", linkLibs.c_str());
958
959
    /*    if ( use_win32 )
960
          {
961
          tgt->SetProperty("WIN32_EXECUTABLE", "ON");
962
          }
963
        if ( use_macbundle)
964
          {
965
          tgt->SetProperty("MACOSX_BUNDLE", "ON");
966
          }*/
967
0
  }
968
969
0
  return packageFound;
970
0
}
971
972
void cmake::LoadEnvironmentPresets()
973
0
{
974
0
  std::string envGenVar;
975
0
  bool hasEnvironmentGenerator = false;
976
0
  if (cmSystemTools::GetEnv("CMAKE_GENERATOR", envGenVar)) {
977
0
    hasEnvironmentGenerator = true;
978
0
    this->EnvironmentGenerator = envGenVar;
979
0
  }
980
981
0
  auto readGeneratorVar = [&](std::string const& name, std::string& key) {
982
0
    std::string varValue;
983
0
    if (cmSystemTools::GetEnv(name, varValue)) {
984
0
      if (hasEnvironmentGenerator) {
985
0
        key = varValue;
986
0
      } else if (!this->GetIsInTryCompile()) {
987
0
        std::string message =
988
0
          cmStrCat("Warning: Environment variable ", name,
989
0
                   " will be ignored, because CMAKE_GENERATOR is not set.");
990
0
        cmSystemTools::Message(message, "Warning");
991
0
      }
992
0
    }
993
0
  };
994
995
0
  readGeneratorVar("CMAKE_GENERATOR_INSTANCE", this->GeneratorInstance);
996
0
  readGeneratorVar("CMAKE_GENERATOR_PLATFORM", this->GeneratorPlatform);
997
0
  readGeneratorVar("CMAKE_GENERATOR_TOOLSET", this->GeneratorToolset);
998
0
  this->IntermediateDirStrategy =
999
0
    cmSystemTools::GetEnvVar("CMAKE_INTERMEDIATE_DIR_STRATEGY");
1000
0
  this->AutogenIntermediateDirStrategy =
1001
0
    cmSystemTools::GetEnvVar("CMAKE_AUTOGEN_INTERMEDIATE_DIR_STRATEGY");
1002
0
}
1003
1004
// Parse the args
1005
void cmake::SetArgs(std::vector<std::string> const& args)
1006
35
{
1007
35
  this->cmdArgs = args;
1008
35
  bool haveToolset = false;
1009
35
  bool havePlatform = false;
1010
35
  bool haveBArg = false;
1011
35
  bool haveCMLName = false;
1012
35
  std::string possibleUnknownArg;
1013
35
  std::string extraProvidedPath;
1014
35
#if !defined(CMAKE_BOOTSTRAP)
1015
35
  std::string profilingFormat;
1016
35
  std::string profilingOutput;
1017
35
  std::string presetName;
1018
1019
35
  ListPresets listPresets = ListPresets::None;
1020
35
#endif
1021
1022
35
  auto EmptyStringArgLambda = [](std::string const&, cmake* state) -> bool {
1023
0
    state->IssueMessage(
1024
0
      MessageType::WARNING,
1025
0
      "Ignoring empty string (\"\") provided on the command line.");
1026
0
    return true;
1027
0
  };
1028
1029
35
  auto SourceArgLambda = [](std::string const& value, cmake* state) -> bool {
1030
0
    if (value.empty()) {
1031
0
      cmSystemTools::Error("No source directory specified for -S");
1032
0
      return false;
1033
0
    }
1034
0
    state->SetHomeDirectoryViaCommandLine(
1035
0
      cmSystemTools::ToNormalizedPathOnDisk(value));
1036
0
    return true;
1037
0
  };
1038
1039
35
  auto BuildArgLambda = [&](std::string const& value, cmake* state) -> bool {
1040
0
    if (value.empty()) {
1041
0
      cmSystemTools::Error("No build directory specified for -B");
1042
0
      return false;
1043
0
    }
1044
0
    state->SetHomeOutputDirectory(
1045
0
      cmSystemTools::ToNormalizedPathOnDisk(value));
1046
0
    haveBArg = true;
1047
0
    return true;
1048
0
  };
1049
1050
35
  auto PlatformLambda = [&](std::string const& value, cmake* state) -> bool {
1051
0
    if (havePlatform) {
1052
0
      cmSystemTools::Error("Multiple -A options not allowed");
1053
0
      return false;
1054
0
    }
1055
0
    state->SetGeneratorPlatform(value);
1056
0
    havePlatform = true;
1057
0
    return true;
1058
0
  };
1059
1060
35
  auto ToolsetLambda = [&](std::string const& value, cmake* state) -> bool {
1061
0
    if (haveToolset) {
1062
0
      cmSystemTools::Error("Multiple -T options not allowed");
1063
0
      return false;
1064
0
    }
1065
0
    state->SetGeneratorToolset(value);
1066
0
    haveToolset = true;
1067
0
    return true;
1068
0
  };
1069
1070
35
  auto CMakeListsFileLambda = [&](std::string const& value,
1071
35
                                  cmake* state) -> bool {
1072
0
    if (haveCMLName) {
1073
0
      cmSystemTools::Error("Multiple --project-file options not allowed");
1074
0
      return false;
1075
0
    }
1076
0
    state->SetCMakeListName(value);
1077
0
    haveCMLName = true;
1078
0
    return true;
1079
0
  };
1080
1081
35
  std::vector<CommandArgument> arguments = {
1082
35
    CommandArgument{ "", CommandArgument::Values::Zero, EmptyStringArgLambda },
1083
35
    CommandArgument{ "-S", "No source directory specified for -S",
1084
35
                     CommandArgument::Values::One,
1085
35
                     CommandArgument::RequiresSeparator::No, SourceArgLambda },
1086
35
    CommandArgument{ "-H", "No source directory specified for -H",
1087
35
                     CommandArgument::Values::One,
1088
35
                     CommandArgument::RequiresSeparator::No, SourceArgLambda },
1089
35
    CommandArgument{ "-O", CommandArgument::Values::Zero,
1090
35
                     IgnoreAndTrueLambda },
1091
35
    CommandArgument{ "-B", "No build directory specified for -B",
1092
35
                     CommandArgument::Values::One,
1093
35
                     CommandArgument::RequiresSeparator::No, BuildArgLambda },
1094
35
    CommandArgument{ "--fresh", CommandArgument::Values::Zero,
1095
35
                     [](std::string const&, cmake* cm) -> bool {
1096
0
                       cm->FreshCache = true;
1097
0
                       return true;
1098
0
                     } },
1099
35
    CommandArgument{ "-P", "-P must be followed by a file name.",
1100
35
                     CommandArgument::Values::One,
1101
35
                     CommandArgument::RequiresSeparator::No,
1102
35
                     IgnoreAndTrueLambda },
1103
35
    CommandArgument{ "-D", "-D must be followed with VAR=VALUE.",
1104
35
                     CommandArgument::Values::One,
1105
35
                     CommandArgument::RequiresSeparator::No,
1106
35
                     IgnoreAndTrueLambda },
1107
35
    CommandArgument{ "-C", "-C must be followed by a file name.",
1108
35
                     CommandArgument::Values::One,
1109
35
                     CommandArgument::RequiresSeparator::No,
1110
35
                     IgnoreAndTrueLambda },
1111
35
    CommandArgument{
1112
35
      "-U", "-U must be followed with VAR.", CommandArgument::Values::One,
1113
35
      CommandArgument::RequiresSeparator::No, IgnoreAndTrueLambda },
1114
35
    CommandArgument{ "-W", "-W must be followed with [no-]<name>.",
1115
35
                     CommandArgument::Values::One,
1116
35
                     CommandArgument::RequiresSeparator::No,
1117
35
                     IgnoreAndTrueLambda },
1118
35
    CommandArgument{ "-A", "No platform specified for -A",
1119
35
                     CommandArgument::Values::One,
1120
35
                     CommandArgument::RequiresSeparator::No, PlatformLambda },
1121
35
    CommandArgument{ "-T", "No toolset specified for -T",
1122
35
                     CommandArgument::Values::One,
1123
35
                     CommandArgument::RequiresSeparator::No, ToolsetLambda },
1124
35
    CommandArgument{ "--toolchain", "No file specified for --toolchain",
1125
35
                     CommandArgument::Values::One, IgnoreAndTrueLambda },
1126
35
    CommandArgument{ "--install-prefix",
1127
35
                     "No install directory specified for --install-prefix",
1128
35
                     CommandArgument::Values::One, IgnoreAndTrueLambda },
1129
1130
35
    CommandArgument{ "--check-build-system", CommandArgument::Values::Two,
1131
35
                     [](std::string const& value, cmake* state) -> bool {
1132
0
                       cmList values{ value };
1133
0
                       state->CheckBuildSystemArgument = values[0];
1134
0
                       state->ClearBuildSystem = (atoi(values[1].c_str()) > 0);
1135
0
                       return true;
1136
0
                     } },
1137
35
    CommandArgument{ "--check-stamp-file", CommandArgument::Values::One,
1138
35
                     [](std::string const& value, cmake* state) -> bool {
1139
0
                       state->CheckStampFile = value;
1140
0
                       return true;
1141
0
                     } },
1142
35
    CommandArgument{ "--check-stamp-list", CommandArgument::Values::One,
1143
35
                     [](std::string const& value, cmake* state) -> bool {
1144
0
                       state->CheckStampList = value;
1145
0
                       return true;
1146
0
                     } },
1147
35
    CommandArgument{ "--regenerate-during-build",
1148
35
                     CommandArgument::Values::Zero,
1149
35
                     [](std::string const&, cmake* state) -> bool {
1150
0
                       state->RegenerateDuringBuild = true;
1151
0
                       return true;
1152
0
                     } },
1153
1154
35
    CommandArgument{ "--find-package", CommandArgument::Values::Zero,
1155
35
                     IgnoreAndTrueLambda },
1156
1157
35
    CommandArgument{ "--graphviz", "No file specified for --graphviz",
1158
35
                     CommandArgument::Values::One,
1159
35
                     [](std::string const& value, cmake* state) -> bool {
1160
0
                       state->SetGraphVizFile(
1161
0
                         cmSystemTools::ToNormalizedPathOnDisk(value));
1162
0
                       return true;
1163
0
                     } },
1164
1165
35
    CommandArgument{ "--debug-trycompile", CommandArgument::Values::Zero,
1166
35
                     [](std::string const&, cmake* state) -> bool {
1167
0
                       std::cout << "debug trycompile on\n";
1168
0
                       state->DebugTryCompileOn();
1169
0
                       return true;
1170
0
                     } },
1171
35
    CommandArgument{ "--debug-output", CommandArgument::Values::Zero,
1172
35
                     [](std::string const&, cmake* state) -> bool {
1173
0
                       std::cout << "Running with debug output on.\n";
1174
0
                       state->SetDebugOutputOn(true);
1175
0
                       return true;
1176
0
                     } },
1177
1178
35
    CommandArgument{ "--log-level", "Invalid level specified for --log-level",
1179
35
                     CommandArgument::Values::One,
1180
35
                     [](std::string const& value, cmake* state) -> bool {
1181
0
                       auto const logLevel = StringToLogLevel(value);
1182
0
                       if (logLevel == Message::LogLevel::LOG_UNDEFINED) {
1183
0
                         cmSystemTools::Error(
1184
0
                           "Invalid level specified for --log-level");
1185
0
                         return false;
1186
0
                       }
1187
0
                       state->SetLogLevel(logLevel);
1188
0
                       state->LogLevelWasSetViaCLI = true;
1189
0
                       return true;
1190
0
                     } },
1191
    // This is supported for backward compatibility. This option only
1192
    // appeared in the 3.15.x release series and was renamed to
1193
    // --log-level in 3.16.0
1194
35
    CommandArgument{ "--loglevel", "Invalid level specified for --loglevel",
1195
35
                     CommandArgument::Values::One,
1196
35
                     [](std::string const& value, cmake* state) -> bool {
1197
0
                       auto const logLevel = StringToLogLevel(value);
1198
0
                       if (logLevel == Message::LogLevel::LOG_UNDEFINED) {
1199
0
                         cmSystemTools::Error(
1200
0
                           "Invalid level specified for --loglevel");
1201
0
                         return false;
1202
0
                       }
1203
0
                       state->SetLogLevel(logLevel);
1204
0
                       state->LogLevelWasSetViaCLI = true;
1205
0
                       return true;
1206
0
                     } },
1207
1208
35
    CommandArgument{ "--log-context", CommandArgument::Values::Zero,
1209
35
                     [](std::string const&, cmake* state) -> bool {
1210
0
                       state->SetShowLogContext(true);
1211
0
                       return true;
1212
0
                     } },
1213
35
    CommandArgument{ "--project-file",
1214
35
                     "No filename specified for --project-file",
1215
35
                     CommandArgument::Values::One, CMakeListsFileLambda },
1216
35
    CommandArgument{
1217
35
      "--debug-find", CommandArgument::Values::Zero,
1218
35
      [](std::string const&, cmake* state) -> bool {
1219
0
        std::cout << "Running with debug output on for the `find` commands.\n";
1220
0
        state->SetDebugFindOutput(true);
1221
0
        return true;
1222
0
      } },
1223
35
    CommandArgument{
1224
35
      "--debug-find-pkg", "Provide a package argument for --debug-find-pkg",
1225
35
      CommandArgument::Values::One, CommandArgument::RequiresSeparator::Yes,
1226
35
      [](std::string const& value, cmake* state) -> bool {
1227
0
        std::vector<std::string> find_pkgs(cmTokenize(value, ','));
1228
0
        std::cout << "Running with debug output on for the 'find' commands "
1229
0
                     "for package(s)";
1230
0
        for (auto const& v : find_pkgs) {
1231
0
          std::cout << ' ' << v;
1232
0
          state->SetDebugFindOutputPkgs(v);
1233
0
        }
1234
0
        std::cout << ".\n";
1235
0
        return true;
1236
0
      } },
1237
35
    CommandArgument{
1238
35
      "--debug-find-var", CommandArgument::Values::One,
1239
35
      CommandArgument::RequiresSeparator::Yes,
1240
35
      [](std::string const& value, cmake* state) -> bool {
1241
0
        std::vector<std::string> find_vars(cmTokenize(value, ','));
1242
0
        std::cout << "Running with debug output on for the variable(s)";
1243
0
        for (auto const& v : find_vars) {
1244
0
          std::cout << ' ' << v;
1245
0
          state->SetDebugFindOutputVars(v);
1246
0
        }
1247
0
        std::cout << ".\n";
1248
0
        return true;
1249
0
      } },
1250
35
    CommandArgument{ "--trace", CommandArgument::Values::Zero,
1251
35
                     [](std::string const&, cmake* state) -> bool {
1252
0
                       std::cout << "Put cmake in trace mode.\n";
1253
0
                       state->SetTrace(true);
1254
0
                       state->SetTraceExpand(false);
1255
0
                       return true;
1256
0
                     } },
1257
35
    CommandArgument{ "--trace-expand", CommandArgument::Values::Zero,
1258
35
                     [](std::string const&, cmake* state) -> bool {
1259
0
                       std::cout << "Put cmake in trace mode, but with "
1260
0
                                    "variables expanded.\n";
1261
0
                       state->SetTrace(true);
1262
0
                       state->SetTraceExpand(true);
1263
0
                       return true;
1264
0
                     } },
1265
35
    CommandArgument{
1266
35
      "--trace-format", "Invalid format specified for --trace-format",
1267
35
      CommandArgument::Values::One,
1268
35
      [](std::string const& value, cmake* state) -> bool {
1269
0
        std::cout << "Put cmake in trace mode and sets the "
1270
0
                     "trace output format.\n";
1271
0
        state->SetTrace(true);
1272
0
        auto const traceFormat = StringToTraceFormat(value);
1273
0
        if (traceFormat == TraceFormat::Undefined) {
1274
0
          cmSystemTools::Error("Invalid format specified for --trace-format. "
1275
0
                               "Valid formats are human, json-v1.");
1276
0
          return false;
1277
0
        }
1278
0
        state->SetTraceFormat(traceFormat);
1279
0
        return true;
1280
0
      } },
1281
35
    CommandArgument{ "--trace-source", "No file specified for --trace-source",
1282
35
                     CommandArgument::Values::OneOrMore,
1283
35
                     [](std::string const& values, cmake* state) -> bool {
1284
0
                       std::cout << "Put cmake in trace mode, but output only "
1285
0
                                    "lines of a specified file. Multiple "
1286
0
                                    "options are allowed.\n";
1287
0
                       for (auto file :
1288
0
                            cmSystemTools::SplitString(values, ';')) {
1289
0
                         cmSystemTools::ConvertToUnixSlashes(file);
1290
0
                         state->AddTraceSource(file);
1291
0
                       }
1292
0
                       state->SetTrace(true);
1293
0
                       return true;
1294
0
                     } },
1295
35
    CommandArgument{ "--trace-redirect",
1296
35
                     "No file specified for --trace-redirect",
1297
35
                     CommandArgument::Values::One,
1298
35
                     [](std::string const& value, cmake* state) -> bool {
1299
0
                       std::cout
1300
0
                         << "Put cmake in trace mode and redirect trace "
1301
0
                            "output to a file instead of stderr.\n";
1302
0
                       std::string file(value);
1303
0
                       cmSystemTools::ConvertToUnixSlashes(file);
1304
0
                       state->SetTraceFile(file);
1305
0
                       state->SetTrace(true);
1306
0
                       return true;
1307
0
                     } },
1308
35
    CommandArgument{
1309
35
      "--warn-uninitialized", CommandArgument::Values::Zero,
1310
35
      [](std::string const&, cmake* state) -> bool {
1311
0
        warnDeprecated("--warn-uninitialized"_s, "-Wuninitialized"_s);
1312
0
        state->CurrentSnapshot.PromoteDiagnostic(
1313
0
          cmDiagnostics::CMD_UNINITIALIZED, cmDiagnostics::Warn, true);
1314
0
        return true;
1315
0
      } },
1316
35
    CommandArgument{ "--warn-unused-vars", CommandArgument::Values::Zero,
1317
35
                     IgnoreAndTrueLambda }, // Option was removed.
1318
35
    CommandArgument{
1319
35
      "--no-warn-unused-cli", CommandArgument::Values::Zero,
1320
35
      [](std::string const&, cmake* state) -> bool {
1321
0
        warnDeprecated("--no-warn-unused-cli"_s, "-Wno-unused-cli"_s);
1322
0
        state->CurrentSnapshot.DemoteDiagnostic(cmDiagnostics::CMD_UNUSED_CLI,
1323
0
                                                cmDiagnostics::Ignore, true);
1324
0
        return true;
1325
0
      } },
1326
35
    CommandArgument{
1327
35
      "--check-system-vars", CommandArgument::Values::Zero,
1328
35
      [](std::string const&, cmake* state) -> bool {
1329
0
        std::cout << "Also check system files when warning about unused and "
1330
0
                     "uninitialized variables.\n";
1331
0
        state->SetCheckSystemVars(true);
1332
0
        return true;
1333
0
      } },
1334
35
    CommandArgument{
1335
35
      "--compile-no-warning-as-error", CommandArgument::Values::Zero,
1336
35
      [](std::string const&, cmake* state) -> bool {
1337
0
        std::cout << "Ignoring COMPILE_WARNING_AS_ERROR target property and "
1338
0
                     "CMAKE_COMPILE_WARNING_AS_ERROR variable.\n";
1339
0
        state->SetIgnoreCompileWarningAsError(true);
1340
0
        return true;
1341
0
      } },
1342
35
    CommandArgument{
1343
35
      "--link-no-warning-as-error", CommandArgument::Values::Zero,
1344
35
      [](std::string const&, cmake* state) -> bool {
1345
0
        std::cout << "Ignoring LINK_WARNING_AS_ERROR target property and "
1346
0
                     "CMAKE_LINK_WARNING_AS_ERROR variable.\n";
1347
0
        state->SetIgnoreLinkWarningAsError(true);
1348
0
        return true;
1349
0
      } },
1350
35
#ifndef CMAKE_BOOTSTRAP
1351
35
    CommandArgument{ "--sarif-output", "No file specified for --sarif-output",
1352
35
                     CommandArgument::Values::One,
1353
35
                     [](std::string const& value, cmake* state) -> bool {
1354
0
                       state->SarifFilePath =
1355
0
                         cmSystemTools::ToNormalizedPathOnDisk(value);
1356
0
                       state->SarifFileOutput = true;
1357
0
                       return true;
1358
0
                     } },
1359
35
#endif
1360
35
    CommandArgument{ "--debugger", CommandArgument::Values::Zero,
1361
35
                     [](std::string const&, cmake* state) -> bool {
1362
0
#ifdef CMake_ENABLE_DEBUGGER
1363
0
                       std::cout << "Running with debugger on.\n";
1364
0
                       state->SetDebuggerOn(true);
1365
0
                       return true;
1366
#else
1367
                       static_cast<void>(state);
1368
                       cmSystemTools::Error(
1369
                         "CMake was not built with support for --debugger");
1370
                       return false;
1371
#endif
1372
0
                     } },
1373
35
    CommandArgument{ "--debugger-pipe",
1374
35
                     "No path specified for --debugger-pipe",
1375
35
                     CommandArgument::Values::One,
1376
35
                     [](std::string const& value, cmake* state) -> bool {
1377
0
#ifdef CMake_ENABLE_DEBUGGER
1378
0
                       state->DebuggerPipe = value;
1379
0
                       return true;
1380
#else
1381
                       static_cast<void>(value);
1382
                       static_cast<void>(state);
1383
                       cmSystemTools::Error("CMake was not built with support "
1384
                                            "for --debugger-pipe");
1385
                       return false;
1386
#endif
1387
0
                     } },
1388
35
    CommandArgument{ "--debugger-dap-log",
1389
35
                     "No file specified for --debugger-dap-log",
1390
35
                     CommandArgument::Values::One,
1391
35
                     [](std::string const& value, cmake* state) -> bool {
1392
0
#ifdef CMake_ENABLE_DEBUGGER
1393
0
                       state->DebuggerDapLogFile =
1394
0
                         cmSystemTools::ToNormalizedPathOnDisk(value);
1395
0
                       return true;
1396
#else
1397
                       static_cast<void>(value);
1398
                       static_cast<void>(state);
1399
                       cmSystemTools::Error("CMake was not built with support "
1400
                                            "for --debugger-dap-log");
1401
                       return false;
1402
#endif
1403
0
                     } },
1404
35
  };
1405
1406
#if defined(CMAKE_HAVE_VS_GENERATORS)
1407
  arguments.emplace_back("--vs-solution-file", CommandArgument::Values::One,
1408
                         [](std::string const& value, cmake* state) -> bool {
1409
                           state->VSSolutionFile = value;
1410
                           return true;
1411
                         });
1412
#endif
1413
1414
35
#if !defined(CMAKE_BOOTSTRAP)
1415
35
  arguments.emplace_back("--profiling-format",
1416
35
                         "No format specified for --profiling-format",
1417
35
                         CommandArgument::Values::One,
1418
35
                         [&](std::string const& value, cmake*) -> bool {
1419
0
                           profilingFormat = value;
1420
0
                           return true;
1421
0
                         });
1422
35
  arguments.emplace_back(
1423
35
    "--profiling-output", "No path specified for --profiling-output",
1424
35
    CommandArgument::Values::One,
1425
35
    [&profilingOutput](std::string const& value, cmake*) -> bool {
1426
0
      profilingOutput = cmSystemTools::ToNormalizedPathOnDisk(value);
1427
0
      return true;
1428
0
    });
1429
35
  arguments.emplace_back("--preset", "No preset specified for --preset",
1430
35
                         CommandArgument::Values::One,
1431
35
                         [&](std::string const& value, cmake*) -> bool {
1432
0
                           presetName = value;
1433
0
                           return true;
1434
0
                         });
1435
35
  arguments.emplace_back(
1436
35
    "--list-presets", CommandArgument::Values::ZeroOrOne,
1437
35
    [&](std::string const& value, cmake*) -> bool {
1438
0
      if (value.empty() || value == "configure") {
1439
0
        listPresets = ListPresets::Configure;
1440
0
      } else if (value == "build") {
1441
0
        listPresets = ListPresets::Build;
1442
0
      } else if (value == "test") {
1443
0
        listPresets = ListPresets::Test;
1444
0
      } else if (value == "package") {
1445
0
        listPresets = ListPresets::Package;
1446
0
      } else if (value == "workflow") {
1447
0
        listPresets = ListPresets::Workflow;
1448
0
      } else if (value == "all") {
1449
0
        listPresets = ListPresets::All;
1450
0
      } else {
1451
0
        cmSystemTools::Error(
1452
0
          "Invalid value specified for --list-presets.\n"
1453
0
          "Valid values are configure, build, test, package, or all. "
1454
0
          "When no value is passed the default is configure.");
1455
0
        return false;
1456
0
      }
1457
1458
0
      return true;
1459
0
    });
1460
1461
35
#endif
1462
1463
35
  bool badGeneratorName = false;
1464
35
  CommandArgument generatorCommand(
1465
35
    "-G", "No generator specified for -G", CommandArgument::Values::One,
1466
35
    CommandArgument::RequiresSeparator::No,
1467
35
    [&](std::string const& value, cmake* state) -> bool {
1468
0
      bool valid = state->CreateAndSetGlobalGenerator(value);
1469
0
      badGeneratorName = !valid;
1470
0
      return valid;
1471
0
    });
1472
1473
70
  for (decltype(args.size()) i = 1; i < args.size(); ++i) {
1474
    // iterate each argument
1475
35
    std::string const& arg = args[i];
1476
1477
35
    if (this->State->GetRole() == cmState::Role::Script && arg == "--") {
1478
      // Stop processing CMake args and avoid possible errors
1479
      // when arbitrary args are given to CMake script.
1480
0
      break;
1481
0
    }
1482
1483
    // Generator flag has special handling for when to print help
1484
    // so it becomes the exception
1485
35
    if (generatorCommand.matches(arg)) {
1486
0
      bool parsed = generatorCommand.parse(arg, i, args, this);
1487
0
      if (!parsed && !badGeneratorName) {
1488
0
        this->PrintGeneratorList();
1489
0
        return;
1490
0
      }
1491
0
      continue;
1492
0
    }
1493
1494
35
    bool matched = false;
1495
35
    bool parsedCorrectly = true; // needs to be true so we can ignore
1496
                                 // arguments so as -E
1497
245
    for (auto const& m : arguments) {
1498
245
      if (m.matches(arg)) {
1499
35
        matched = true;
1500
35
        parsedCorrectly = m.parse(arg, i, args, this);
1501
35
        break;
1502
35
      }
1503
245
    }
1504
1505
    // We have an issue where arguments to a "-P" script mode
1506
    // can be provided before the "-P" argument. This means
1507
    // that we need to lazily check this argument after checking
1508
    // all args.
1509
    // Additionally it can't be the source/binary tree location
1510
35
    if (!parsedCorrectly) {
1511
0
      cmSystemTools::Error("Run 'cmake --help' for all supported options.");
1512
0
      exit(1);
1513
35
    } else if (!matched && cmHasPrefix(arg, '-')) {
1514
0
      possibleUnknownArg = arg;
1515
35
    } else if (!matched) {
1516
0
      bool parsedDirectory = this->SetDirectoriesFromFile(arg);
1517
0
      if (!parsedDirectory) {
1518
0
        extraProvidedPath = arg;
1519
0
      }
1520
0
    }
1521
35
  }
1522
1523
35
  if (!extraProvidedPath.empty() &&
1524
0
      this->State->GetRole() == cmState::Role::Project) {
1525
0
    this->IssueMessage(MessageType::WARNING,
1526
0
                       cmStrCat("Ignoring extra path from command line:\n \"",
1527
0
                                extraProvidedPath, '"'));
1528
0
  }
1529
35
  if (!possibleUnknownArg.empty() &&
1530
0
      this->State->GetRole() != cmState::Role::Script) {
1531
0
    cmSystemTools::Error(cmStrCat("Unknown argument ", possibleUnknownArg));
1532
0
    cmSystemTools::Error("Run 'cmake --help' for all supported options.");
1533
0
    exit(1);
1534
0
  }
1535
1536
  // Empty instance, platform and toolset if only a generator is specified
1537
35
  if (this->GlobalGenerator) {
1538
0
    this->GeneratorInstance = "";
1539
0
    if (!this->GeneratorPlatformSet) {
1540
0
      this->GeneratorPlatform = "";
1541
0
    }
1542
0
    if (!this->GeneratorToolsetSet) {
1543
0
      this->GeneratorToolset = "";
1544
0
    }
1545
0
  }
1546
1547
35
#if !defined(CMAKE_BOOTSTRAP)
1548
35
  if (!profilingOutput.empty() || !profilingFormat.empty()) {
1549
0
    if (profilingOutput.empty()) {
1550
0
      cmSystemTools::Error(
1551
0
        "--profiling-format specified but no --profiling-output!");
1552
0
      return;
1553
0
    }
1554
0
    if (profilingFormat == "google-trace"_s) {
1555
0
      try {
1556
0
        this->ProfilingOutput =
1557
0
          cm::make_unique<cmMakefileProfilingData>(profilingOutput);
1558
0
      } catch (std::runtime_error& e) {
1559
0
        cmSystemTools::Error(
1560
0
          cmStrCat("Could not start profiling: ", e.what()));
1561
0
        return;
1562
0
      }
1563
0
    } else {
1564
0
      cmSystemTools::Error("Invalid format specified for --profiling-format");
1565
0
      return;
1566
0
    }
1567
0
  }
1568
35
#endif
1569
1570
35
  bool const haveSourceDir = !this->GetHomeDirectory().empty();
1571
35
  bool const haveBinaryDir = !this->GetHomeOutputDirectory().empty();
1572
35
  bool const havePreset =
1573
#ifdef CMAKE_BOOTSTRAP
1574
    false;
1575
#else
1576
35
    !presetName.empty();
1577
35
#endif
1578
1579
35
  if (this->State->GetRole() == cmState::Role::Project && !haveSourceDir &&
1580
0
      !haveBinaryDir && !havePreset) {
1581
0
    this->IssueMessage(
1582
0
      MessageType::WARNING,
1583
0
      "No source or binary directory provided. Both will be assumed to be "
1584
0
      "the same as the current working directory, but note that this "
1585
0
      "warning will become a fatal error in future CMake releases.");
1586
0
  }
1587
1588
35
  if (!haveSourceDir) {
1589
0
    this->SetHomeDirectory(cmSystemTools::GetLogicalWorkingDirectory());
1590
0
  }
1591
35
  if (!haveBinaryDir) {
1592
0
    this->SetHomeOutputDirectory(cmSystemTools::GetLogicalWorkingDirectory());
1593
0
  }
1594
1595
35
#if !defined(CMAKE_BOOTSTRAP)
1596
35
  if (listPresets != ListPresets::None || !presetName.empty()) {
1597
0
    this->SetArgsFromPreset(presetName, listPresets, haveBArg);
1598
0
  }
1599
35
#endif
1600
35
}
1601
1602
namespace {
1603
using LevelsPair = std::pair<cm::string_view, Message::LogLevel>;
1604
using LevelsPairArray = std::array<LevelsPair, 7>;
1605
LevelsPairArray const& getStringToLogLevelPairs()
1606
0
{
1607
0
  static LevelsPairArray const levels = {
1608
0
    { { "error", Message::LogLevel::LOG_ERROR },
1609
0
      { "warning", Message::LogLevel::LOG_WARNING },
1610
0
      { "notice", Message::LogLevel::LOG_NOTICE },
1611
0
      { "status", Message::LogLevel::LOG_STATUS },
1612
0
      { "verbose", Message::LogLevel::LOG_VERBOSE },
1613
0
      { "debug", Message::LogLevel::LOG_DEBUG },
1614
0
      { "trace", Message::LogLevel::LOG_TRACE } }
1615
0
  };
1616
0
  return levels;
1617
0
}
1618
} // namespace
1619
1620
Message::LogLevel cmake::StringToLogLevel(cm::string_view levelStr)
1621
0
{
1622
0
  LevelsPairArray const& levels = getStringToLogLevelPairs();
1623
1624
0
  auto const levelStrLowCase =
1625
0
    cmSystemTools::LowerCase(std::string{ levelStr });
1626
1627
  // NOLINTNEXTLINE(readability-qualified-auto)
1628
0
  auto const it = std::find_if(levels.cbegin(), levels.cend(),
1629
0
                               [&levelStrLowCase](LevelsPair const& p) {
1630
0
                                 return p.first == levelStrLowCase;
1631
0
                               });
1632
0
  return (it != levels.cend()) ? it->second : Message::LogLevel::LOG_UNDEFINED;
1633
0
}
1634
1635
std::string cmake::LogLevelToString(Message::LogLevel level)
1636
0
{
1637
0
  LevelsPairArray const& levels = getStringToLogLevelPairs();
1638
1639
  // NOLINTNEXTLINE(readability-qualified-auto)
1640
0
  auto const it =
1641
0
    std::find_if(levels.cbegin(), levels.cend(),
1642
0
                 [&level](LevelsPair const& p) { return p.second == level; });
1643
0
  cm::string_view const levelStrLowerCase =
1644
0
    (it != levels.cend()) ? it->first : "undefined";
1645
0
  std::string levelStrUpperCase =
1646
0
    cmSystemTools::UpperCase(std::string{ levelStrLowerCase });
1647
0
  return levelStrUpperCase;
1648
0
}
1649
1650
cmake::TraceFormat cmake::StringToTraceFormat(std::string const& traceStr)
1651
0
{
1652
0
  using TracePair = std::pair<std::string, TraceFormat>;
1653
0
  static std::vector<TracePair> const levels = {
1654
0
    { "human", TraceFormat::Human },
1655
0
    { "json-v1", TraceFormat::JSONv1 },
1656
0
  };
1657
1658
0
  auto const traceStrLowCase = cmSystemTools::LowerCase(traceStr);
1659
1660
0
  auto const it = std::find_if(levels.cbegin(), levels.cend(),
1661
0
                               [&traceStrLowCase](TracePair const& p) {
1662
0
                                 return p.first == traceStrLowCase;
1663
0
                               });
1664
0
  return (it != levels.cend()) ? it->second : TraceFormat::Undefined;
1665
0
}
1666
1667
bool cmake::PopTraceCmd()
1668
0
{
1669
0
  if (this->cmakeLangTraceCmdStack.empty()) {
1670
    // Nothing to pop! A caller should report an error.
1671
0
    return false;
1672
0
  }
1673
0
  this->cmakeLangTraceCmdStack.pop();
1674
0
  return true;
1675
0
}
1676
1677
void cmake::SetTraceFile(std::string const& file)
1678
0
{
1679
0
  this->TraceFile.close();
1680
0
  this->TraceFile.open(file.c_str());
1681
0
  if (!this->TraceFile) {
1682
0
    cmSystemTools::Error(cmStrCat("Error opening trace file ", file, ": ",
1683
0
                                  cmSystemTools::GetLastSystemError()));
1684
0
    return;
1685
0
  }
1686
0
  std::cout << "Trace will be written to " << file << '\n';
1687
0
}
1688
1689
void cmake::PrintTraceFormatVersion()
1690
0
{
1691
0
  if (!this->GetTrace()) {
1692
0
    return;
1693
0
  }
1694
1695
0
  std::string msg;
1696
1697
0
  switch (this->GetTraceFormat()) {
1698
0
    case TraceFormat::JSONv1: {
1699
0
#ifndef CMAKE_BOOTSTRAP
1700
0
      Json::Value val;
1701
0
      Json::Value version;
1702
0
      Json::StreamWriterBuilder builder;
1703
0
      builder["indentation"] = "";
1704
0
      version["major"] = 1;
1705
0
      version["minor"] = 2;
1706
0
      val["version"] = version;
1707
0
      msg = Json::writeString(builder, val);
1708
0
#endif
1709
0
      break;
1710
0
    }
1711
0
    case TraceFormat::Human:
1712
0
      msg = "";
1713
0
      break;
1714
0
    case TraceFormat::Undefined:
1715
0
      msg = "INTERNAL ERROR: Trace format is Undefined";
1716
0
      break;
1717
0
  }
1718
1719
0
  if (msg.empty()) {
1720
0
    return;
1721
0
  }
1722
1723
0
  auto& f = this->GetTraceFile();
1724
0
  if (f) {
1725
0
    f << msg << '\n';
1726
0
  } else {
1727
0
    cmSystemTools::Message(msg);
1728
0
  }
1729
0
}
1730
1731
void cmake::SetTraceRedirect(cmake* other)
1732
0
{
1733
0
  this->Trace = other->Trace;
1734
0
  this->TraceExpand = other->TraceExpand;
1735
0
  this->TraceFormatVar = other->TraceFormatVar;
1736
0
  this->TraceOnlyThisSources = other->TraceOnlyThisSources;
1737
1738
0
  this->TraceRedirect = other;
1739
0
}
1740
1741
bool cmake::SetDirectoriesFromFile(std::string const& arg)
1742
0
{
1743
  // Check if the argument refers to a CMakeCache.txt or CMakeLists.txt file.
1744
  // Do not check for the custom project filename CMAKE_LIST_FILE_NAME, as it
1745
  // cannot be determined until after reading the CMakeCache.txt
1746
0
  std::string listPath;
1747
0
  std::string cachePath;
1748
0
  bool is_source_dir = false;
1749
0
  bool is_empty_directory = false;
1750
0
  if (cmSystemTools::FileIsDirectory(arg)) {
1751
0
    std::string path = cmSystemTools::ToNormalizedPathOnDisk(arg);
1752
0
    std::string cacheFile = cmStrCat(path, "/CMakeCache.txt");
1753
0
    std::string listFile = this->GetCMakeListFile(path);
1754
1755
0
    is_empty_directory = true;
1756
0
    if (cmSystemTools::FileExists(cacheFile)) {
1757
0
      cachePath = path;
1758
0
      is_empty_directory = false;
1759
0
    }
1760
0
    if (cmSystemTools::FileExists(listFile)) {
1761
0
      listPath = path;
1762
0
      is_empty_directory = false;
1763
0
      is_source_dir = true;
1764
0
    }
1765
0
  } else if (cmSystemTools::FileExists(arg)) {
1766
0
    std::string fullPath = cmSystemTools::ToNormalizedPathOnDisk(arg);
1767
0
    std::string name = cmSystemTools::GetFilenameName(fullPath);
1768
0
    name = cmSystemTools::LowerCase(name);
1769
0
    if (name == "cmakecache.txt"_s) {
1770
0
      cachePath = cmSystemTools::GetFilenamePath(fullPath);
1771
0
    } else if (name == "cmakelists.txt"_s) {
1772
0
      listPath = cmSystemTools::GetFilenamePath(fullPath);
1773
0
    }
1774
0
  } else {
1775
    // Specified file or directory does not exist.  Try to set things
1776
    // up to produce a meaningful error message.
1777
0
    std::string fullPath = cmSystemTools::CollapseFullPath(arg);
1778
0
    std::string name = cmSystemTools::GetFilenameName(fullPath);
1779
0
    name = cmSystemTools::LowerCase(name);
1780
0
    if (name == "cmakecache.txt"_s || name == "cmakelists.txt"_s) {
1781
0
      listPath = cmSystemTools::GetFilenamePath(fullPath);
1782
0
    } else {
1783
0
      listPath = fullPath;
1784
0
    }
1785
0
  }
1786
1787
  // If there is a CMakeCache.txt file, use its settings.
1788
0
  if (!cachePath.empty()) {
1789
0
    if (this->LoadCache(cachePath)) {
1790
0
      cmValue existingValue =
1791
0
        this->State->GetCacheEntryValue("CMAKE_HOME_DIRECTORY");
1792
0
      if (existingValue && !existingValue.IsEmpty()) {
1793
0
        this->SetHomeOutputDirectory(cachePath);
1794
0
        this->SetHomeDirectory(*existingValue);
1795
0
        return true;
1796
0
      }
1797
0
    }
1798
0
  }
1799
1800
0
  bool no_source_tree = this->GetHomeDirectory().empty();
1801
0
  bool no_build_tree = this->GetHomeOutputDirectory().empty();
1802
1803
  // When invoked with a path that points to an existing CMakeCache
1804
  // This function is called multiple times with the same path
1805
0
  bool const passed_same_path = (listPath == this->GetHomeDirectory()) ||
1806
0
    (listPath == this->GetHomeOutputDirectory());
1807
0
  bool used_provided_path =
1808
0
    (passed_same_path || is_source_dir || no_build_tree);
1809
1810
  // If there is a CMakeLists.txt file, use it as the source tree.
1811
0
  if (!listPath.empty()) {
1812
    // When invoked with a path that points to an existing CMakeCache
1813
    // This function is called multiple times with the same path
1814
0
    if (is_source_dir) {
1815
0
      this->SetHomeDirectoryViaCommandLine(listPath);
1816
0
      if (no_build_tree) {
1817
0
        this->SetHomeOutputDirectory(
1818
0
          cmSystemTools::GetLogicalWorkingDirectory());
1819
0
      }
1820
0
    } else if (no_source_tree && no_build_tree) {
1821
0
      this->SetHomeDirectory(listPath);
1822
0
      this->SetHomeOutputDirectory(
1823
0
        cmSystemTools::GetLogicalWorkingDirectory());
1824
0
    } else if (no_build_tree) {
1825
0
      this->SetHomeOutputDirectory(listPath);
1826
0
    }
1827
0
  } else {
1828
0
    if (no_source_tree) {
1829
      // We didn't find a CMakeLists.txt and it wasn't specified
1830
      // with -S. Assume it is the path to the source tree
1831
0
      this->SetHomeDirectory(cmSystemTools::ToNormalizedPathOnDisk(arg));
1832
0
    }
1833
0
    if (no_build_tree && !no_source_tree && is_empty_directory) {
1834
      // passed `-S <path> <build_dir> when build_dir is an empty directory
1835
0
      this->SetHomeOutputDirectory(cmSystemTools::ToNormalizedPathOnDisk(arg));
1836
0
    } else if (no_build_tree) {
1837
      // We didn't find a CMakeCache.txt and it wasn't specified
1838
      // with -B. Assume the current working directory as the build tree.
1839
0
      this->SetHomeOutputDirectory(
1840
0
        cmSystemTools::GetLogicalWorkingDirectory());
1841
0
      used_provided_path = false;
1842
0
    }
1843
0
  }
1844
1845
0
  return used_provided_path;
1846
0
}
1847
1848
// at the end of this CMAKE_ROOT and CMAKE_COMMAND should be added to the
1849
// cache
1850
int cmake::AddCMakePaths()
1851
1
{
1852
  // Save the value in the cache
1853
1
  this->AddCacheEntry("CMAKE_COMMAND", cmSystemTools::GetCMakeCommand(),
1854
1
                      "Path to CMake executable.", cmStateEnums::INTERNAL);
1855
1
#ifndef CMAKE_BOOTSTRAP
1856
1
  this->AddCacheEntry("CMAKE_CTEST_COMMAND", cmSystemTools::GetCTestCommand(),
1857
1
                      "Path to ctest program executable.",
1858
1
                      cmStateEnums::INTERNAL);
1859
1
  this->AddCacheEntry("CMAKE_CPACK_COMMAND", cmSystemTools::GetCPackCommand(),
1860
1
                      "Path to cpack program executable.",
1861
1
                      cmStateEnums::INTERNAL);
1862
1
#endif
1863
1
  if (!cmSystemTools::FileExists(
1864
1
        (cmSystemTools::GetCMakeRoot() + "/Modules/CMake.cmake"))) {
1865
    // couldn't find modules
1866
1
    cmSystemTools::Error(
1867
1
      cmStrCat("Could not find CMAKE_ROOT !!!\n"
1868
1
               "CMake has most likely not been installed correctly.\n"
1869
1
               "Modules directory not found in\n",
1870
1
               cmSystemTools::GetCMakeRoot()));
1871
1
    return 0;
1872
1
  }
1873
0
  this->AddCacheEntry("CMAKE_ROOT", cmSystemTools::GetCMakeRoot(),
1874
0
                      "Path to CMake installation.", cmStateEnums::INTERNAL);
1875
1876
0
  return 1;
1877
1
}
1878
1879
void cmake::AddDefaultExtraGenerators()
1880
35
{
1881
35
#if !defined(CMAKE_BOOTSTRAP)
1882
35
  this->ExtraGenerators.push_back(cmExtraCodeBlocksGenerator::GetFactory());
1883
35
  this->ExtraGenerators.push_back(cmExtraCodeLiteGenerator::GetFactory());
1884
35
  this->ExtraGenerators.push_back(cmExtraEclipseCDT4Generator::GetFactory());
1885
35
  this->ExtraGenerators.push_back(cmExtraKateGenerator::GetFactory());
1886
35
  this->ExtraGenerators.push_back(cmExtraSublimeTextGenerator::GetFactory());
1887
35
#endif
1888
35
}
1889
1890
void cmake::GetRegisteredGenerators(
1891
  std::vector<GeneratorInfo>& generators) const
1892
0
{
1893
0
  for (auto const& gen : this->Generators) {
1894
0
    std::vector<std::string> names = gen->GetGeneratorNames();
1895
1896
0
    for (std::string const& name : names) {
1897
0
      GeneratorInfo info;
1898
0
      info.supportsToolset = gen->SupportsToolset();
1899
0
      info.supportsPlatform = gen->SupportsPlatform();
1900
0
      info.supportedPlatforms = gen->GetKnownPlatforms();
1901
0
      info.defaultPlatform = gen->GetDefaultPlatformName();
1902
0
      info.name = name;
1903
0
      info.baseName = name;
1904
0
      info.isAlias = false;
1905
0
      generators.push_back(std::move(info));
1906
0
    }
1907
0
  }
1908
1909
0
  for (cmExternalMakefileProjectGeneratorFactory* eg : this->ExtraGenerators) {
1910
0
    std::vector<std::string> const genList =
1911
0
      eg->GetSupportedGlobalGenerators();
1912
0
    for (std::string const& gen : genList) {
1913
0
      GeneratorInfo info;
1914
0
      info.name = cmExternalMakefileProjectGenerator::CreateFullGeneratorName(
1915
0
        gen, eg->GetName());
1916
0
      info.baseName = gen;
1917
0
      info.extraName = eg->GetName();
1918
0
      info.supportsPlatform = false;
1919
0
      info.supportsToolset = false;
1920
0
      info.isAlias = false;
1921
0
      generators.push_back(std::move(info));
1922
0
    }
1923
0
    for (std::string const& a : eg->Aliases) {
1924
0
      GeneratorInfo info;
1925
0
      info.name = a;
1926
0
      if (!genList.empty()) {
1927
0
        info.baseName = genList.at(0);
1928
0
      }
1929
0
      info.extraName = eg->GetName();
1930
0
      info.supportsPlatform = false;
1931
0
      info.supportsToolset = false;
1932
0
      info.isAlias = true;
1933
0
      generators.push_back(std::move(info));
1934
0
    }
1935
0
  }
1936
0
}
1937
1938
static std::pair<std::unique_ptr<cmExternalMakefileProjectGenerator>,
1939
                 std::string>
1940
createExtraGenerator(
1941
  std::vector<cmExternalMakefileProjectGeneratorFactory*> const& in,
1942
  std::string const& name)
1943
0
{
1944
0
  for (cmExternalMakefileProjectGeneratorFactory* i : in) {
1945
0
    std::vector<std::string> const generators =
1946
0
      i->GetSupportedGlobalGenerators();
1947
0
    if (i->GetName() == name) { // Match aliases
1948
0
      return { i->CreateExternalMakefileProjectGenerator(), generators.at(0) };
1949
0
    }
1950
0
    for (std::string const& g : generators) {
1951
0
      std::string const fullName =
1952
0
        cmExternalMakefileProjectGenerator::CreateFullGeneratorName(
1953
0
          g, i->GetName());
1954
0
      if (fullName == name) {
1955
0
        return { i->CreateExternalMakefileProjectGenerator(), g };
1956
0
      }
1957
0
    }
1958
0
  }
1959
0
  return { nullptr, name };
1960
0
}
1961
1962
std::unique_ptr<cmGlobalGenerator> cmake::CreateGlobalGenerator(
1963
  std::string const& gname)
1964
0
{
1965
0
  std::pair<std::unique_ptr<cmExternalMakefileProjectGenerator>, std::string>
1966
0
    extra = createExtraGenerator(this->ExtraGenerators, gname);
1967
0
  std::unique_ptr<cmExternalMakefileProjectGenerator>& extraGenerator =
1968
0
    extra.first;
1969
0
  std::string const& name = extra.second;
1970
1971
0
  std::unique_ptr<cmGlobalGenerator> generator;
1972
0
  for (auto const& g : this->Generators) {
1973
0
    generator = g->CreateGlobalGenerator(name, this);
1974
0
    if (generator) {
1975
0
      break;
1976
0
    }
1977
0
  }
1978
1979
0
  if (generator) {
1980
0
    generator->SetExternalMakefileProjectGenerator(std::move(extraGenerator));
1981
0
  }
1982
1983
0
  return generator;
1984
0
}
1985
1986
bool cmake::CreateAndSetGlobalGenerator(std::string const& name)
1987
0
{
1988
0
  auto gen = this->CreateGlobalGenerator(name);
1989
0
  if (!gen) {
1990
0
    std::string kdevError;
1991
0
    std::string vsError;
1992
0
    if (name.find("KDevelop3", 0) != std::string::npos) {
1993
0
      kdevError = "\nThe KDevelop3 generator is not supported anymore.";
1994
0
    }
1995
0
    if (cmHasLiteralPrefix(name, "Visual Studio ") &&
1996
0
        name.length() >= cmStrLen("Visual Studio xx xxxx ")) {
1997
0
      vsError = "\nUsing platforms in Visual Studio generator names is not "
1998
0
                "supported in CMakePresets.json.";
1999
0
    }
2000
2001
0
    cmSystemTools::Error(
2002
0
      cmStrCat("Could not create named generator ", name, kdevError, vsError));
2003
0
    this->PrintGeneratorList();
2004
0
    return false;
2005
0
  }
2006
2007
0
  this->SetGlobalGenerator(std::move(gen));
2008
0
  return true;
2009
0
}
2010
2011
#ifndef CMAKE_BOOTSTRAP
2012
bool cmake::SetArgsFromPreset(std::string const& presetName,
2013
                              ListPresets listPresets, bool haveBinaryDirArg)
2014
0
{
2015
0
  cmCMakePresetsGraph presetsGraph;
2016
0
  auto result = presetsGraph.ReadProjectPresets(this->GetHomeDirectory());
2017
0
  if (result != true) {
2018
0
    std::string errorMsg =
2019
0
      cmStrCat("Could not read presets from ", this->GetHomeDirectory(), ":\n",
2020
0
               presetsGraph.parseState.GetErrorMessage());
2021
0
    cmSystemTools::Error(errorMsg);
2022
0
    return false;
2023
0
  }
2024
2025
0
  if (listPresets != ListPresets::None) {
2026
0
    if (listPresets == ListPresets::Configure) {
2027
0
      this->PrintPresetList(presetsGraph);
2028
0
    } else if (listPresets == ListPresets::Build) {
2029
0
      presetsGraph.PrintBuildPresetList();
2030
0
    } else if (listPresets == ListPresets::Test) {
2031
0
      presetsGraph.PrintTestPresetList();
2032
0
    } else if (listPresets == ListPresets::Package) {
2033
0
      presetsGraph.PrintPackagePresetList();
2034
0
    } else if (listPresets == ListPresets::Workflow) {
2035
0
      presetsGraph.PrintWorkflowPresetList();
2036
0
    } else if (listPresets == ListPresets::All) {
2037
0
      presetsGraph.PrintAllPresets();
2038
0
    }
2039
2040
0
    this->State->SetRoleToHelpForListPresets();
2041
0
    return false;
2042
0
  }
2043
2044
0
  auto resolveResult =
2045
0
    presetsGraph.ResolvePreset(presetName, presetsGraph.ConfigurePresets);
2046
0
  using ConfigurePreset = cmCMakePresetsGraph::ConfigurePreset;
2047
0
  using S = cmCMakePresetsGraph::PresetResolveStatus;
2048
0
  auto resolveError = cmCMakePresetsGraph::FormatPresetError<ConfigurePreset>(
2049
0
    resolveResult.StatusCode, resolveResult.ErrorPresetName,
2050
0
    this->GetHomeDirectory());
2051
0
  if (resolveError) {
2052
0
    cmSystemTools::Error(*resolveError);
2053
0
    if (resolveResult.StatusCode == S::NotFound ||
2054
0
        resolveResult.StatusCode == S::Hidden) {
2055
0
      this->PrintPresetList(presetsGraph);
2056
0
    }
2057
0
    return false;
2058
0
  }
2059
0
  auto const* expandedPreset = resolveResult.Preset;
2060
2061
0
  if (!this->State->IsCacheLoaded() && !haveBinaryDirArg &&
2062
0
      !expandedPreset->BinaryDir.empty()) {
2063
0
    this->SetHomeOutputDirectory(expandedPreset->BinaryDir);
2064
0
  }
2065
0
  if (!this->GlobalGenerator && !expandedPreset->Generator.empty()) {
2066
0
    if (!this->CreateAndSetGlobalGenerator(expandedPreset->Generator)) {
2067
0
      return false;
2068
0
    }
2069
0
  }
2070
0
  this->UnprocessedPresetVariables = expandedPreset->CacheVariables;
2071
0
  this->UnprocessedPresetEnvironment = expandedPreset->Environment;
2072
2073
0
  if (!expandedPreset->InstallDir.empty() &&
2074
0
      !this->State->GetInitializedCacheValue("CMAKE_INSTALL_PREFIX")) {
2075
0
    this->UnprocessedPresetVariables["CMAKE_INSTALL_PREFIX"] = {
2076
0
      "PATH", expandedPreset->InstallDir
2077
0
    };
2078
0
  }
2079
0
  if (!expandedPreset->ToolchainFile.empty() &&
2080
0
      !this->State->GetInitializedCacheValue("CMAKE_TOOLCHAIN_FILE")) {
2081
0
    this->UnprocessedPresetVariables["CMAKE_TOOLCHAIN_FILE"] = {
2082
0
      "FILEPATH", expandedPreset->ToolchainFile
2083
0
    };
2084
0
  }
2085
2086
0
  if (!expandedPreset->ArchitectureStrategy ||
2087
0
      expandedPreset->ArchitectureStrategy ==
2088
0
        cmCMakePresetsGraph::ArchToolsetStrategy::Set) {
2089
0
    if (!this->GeneratorPlatformSet && !expandedPreset->Architecture.empty()) {
2090
0
      this->SetGeneratorPlatform(expandedPreset->Architecture);
2091
0
    }
2092
0
  }
2093
0
  if (!expandedPreset->ToolsetStrategy ||
2094
0
      expandedPreset->ToolsetStrategy ==
2095
0
        cmCMakePresetsGraph::ArchToolsetStrategy::Set) {
2096
0
    if (!this->GeneratorToolsetSet && !expandedPreset->Toolset.empty()) {
2097
0
      this->SetGeneratorToolset(expandedPreset->Toolset);
2098
0
    }
2099
0
  }
2100
2101
0
  if (!expandedPreset->GraphVizFile.empty()) {
2102
0
    if (this->GraphVizFile.empty()) {
2103
0
      this->SetGraphVizFile(
2104
0
        cmSystemTools::CollapseFullPath(expandedPreset->GraphVizFile));
2105
0
    }
2106
0
  }
2107
2108
0
  this->SetDiagnosticsFromPreset(expandedPreset->Warnings,
2109
0
                                 expandedPreset->Errors);
2110
0
  if (expandedPreset->WarnSystemVars == true) {
2111
0
    this->SetCheckSystemVars(true);
2112
0
  }
2113
0
  if (expandedPreset->DebugOutput == true) {
2114
0
    this->SetDebugOutputOn(true);
2115
0
  }
2116
0
  if (expandedPreset->DebugTryCompile == true) {
2117
0
    this->DebugTryCompileOn();
2118
0
  }
2119
0
  if (expandedPreset->DebugFind == true) {
2120
0
    this->SetDebugFindOutput(true);
2121
0
  }
2122
0
  if (expandedPreset->TraceMode &&
2123
0
      expandedPreset->TraceMode !=
2124
0
        cmCMakePresetsGraph::TraceEnableMode::Disable) {
2125
0
    this->SetTrace(true);
2126
0
    if (expandedPreset->TraceMode ==
2127
0
        cmCMakePresetsGraph::TraceEnableMode::Expand) {
2128
0
      this->SetTraceExpand(true);
2129
0
    }
2130
0
  }
2131
0
  if (expandedPreset->TraceFormat) {
2132
0
    this->SetTrace(true);
2133
0
    this->SetTraceFormat(*expandedPreset->TraceFormat);
2134
0
  }
2135
0
  if (!expandedPreset->TraceSource.empty()) {
2136
0
    this->SetTrace(true);
2137
0
    for (std::string const& filePaths : expandedPreset->TraceSource) {
2138
0
      this->AddTraceSource(filePaths);
2139
0
    }
2140
0
  }
2141
0
  if (!expandedPreset->TraceRedirect.empty()) {
2142
0
    this->SetTrace(true);
2143
0
    this->SetTraceFile(expandedPreset->TraceRedirect);
2144
0
  }
2145
2146
0
  return true;
2147
0
}
2148
2149
void cmake::PrintPresetList(cmCMakePresetsGraph const& graph) const
2150
0
{
2151
0
  std::vector<GeneratorInfo> generators;
2152
0
  this->GetRegisteredGenerators(generators);
2153
0
  auto filter =
2154
0
    [&generators](cmCMakePresetsGraph::ConfigurePreset const& preset) -> bool {
2155
0
    if (preset.Generator.empty()) {
2156
0
      return true;
2157
0
    }
2158
0
    auto condition = [&preset](GeneratorInfo const& info) -> bool {
2159
0
      return info.name == preset.Generator;
2160
0
    };
2161
0
    auto it = std::find_if(generators.begin(), generators.end(), condition);
2162
0
    return it != generators.end();
2163
0
  };
2164
2165
0
  graph.PrintConfigurePresetList(filter);
2166
0
}
2167
#endif
2168
2169
void cmake::SetHomeDirectoryViaCommandLine(std::string const& path)
2170
0
{
2171
0
  if (path.empty()) {
2172
0
    return;
2173
0
  }
2174
2175
0
  auto prev_path = this->GetHomeDirectory();
2176
0
  if (prev_path != path && !prev_path.empty() &&
2177
0
      this->State->GetRole() == cmState::Role::Project) {
2178
0
    this->IssueMessage(
2179
0
      MessageType::WARNING,
2180
0
      cmStrCat("Ignoring extra path from command line:\n \"", prev_path, '"'));
2181
0
  }
2182
0
  this->SetHomeDirectory(path);
2183
0
}
2184
2185
void cmake::SetHomeDirectory(std::string const& dir)
2186
36
{
2187
36
  assert(!dir.empty());
2188
36
  this->State->SetSourceDirectory(dir);
2189
36
  if (this->CurrentSnapshot.IsValid()) {
2190
36
    this->CurrentSnapshot.SetDefinition("CMAKE_SOURCE_DIR", dir);
2191
36
  }
2192
2193
36
  if (this->State->GetIsTryCompile() == cmState::TryCompile::No) {
2194
36
    this->Messenger->SetTopSource(this->GetHomeDirectory());
2195
36
  } else {
2196
0
    this->Messenger->SetTopSource(cm::nullopt);
2197
0
  }
2198
36
}
2199
2200
std::string const& cmake::GetHomeDirectory() const
2201
72
{
2202
72
  return this->State->GetSourceDirectory();
2203
72
}
2204
2205
void cmake::SetHomeOutputDirectory(std::string const& dir)
2206
36
{
2207
36
  assert(!dir.empty());
2208
36
  this->State->SetBinaryDirectory(dir);
2209
36
  if (this->CurrentSnapshot.IsValid()) {
2210
36
    this->CurrentSnapshot.SetDefinition("CMAKE_BINARY_DIR", dir);
2211
36
  }
2212
36
}
2213
2214
std::string const& cmake::GetHomeOutputDirectory() const
2215
36
{
2216
36
  return this->State->GetBinaryDirectory();
2217
36
}
2218
2219
std::string cmake::FindCacheFile(std::string const& binaryDir)
2220
0
{
2221
0
  std::string cachePath = binaryDir;
2222
0
  cmSystemTools::ConvertToUnixSlashes(cachePath);
2223
0
  std::string cacheFile = cmStrCat(cachePath, "/CMakeCache.txt");
2224
0
  if (!cmSystemTools::FileExists(cacheFile)) {
2225
    // search in parent directories for cache
2226
0
    std::string cmakeFiles = cmStrCat(cachePath, "/CMakeFiles");
2227
0
    if (cmSystemTools::FileExists(cmakeFiles)) {
2228
0
      std::string cachePathFound =
2229
0
        cmSystemTools::FileExistsInParentDirectories("CMakeCache.txt",
2230
0
                                                     cachePath, "/");
2231
0
      if (!cachePathFound.empty()) {
2232
0
        cachePath = cmSystemTools::GetFilenamePath(cachePathFound);
2233
0
      }
2234
0
    }
2235
0
  }
2236
0
  return cachePath;
2237
0
}
2238
2239
void cmake::SetGlobalGenerator(std::unique_ptr<cmGlobalGenerator> gg)
2240
0
{
2241
0
  if (!gg) {
2242
0
    cmSystemTools::Error("Error SetGlobalGenerator called with null");
2243
0
    return;
2244
0
  }
2245
0
  if (this->GlobalGenerator) {
2246
    // restore the original environment variables CXX and CC
2247
0
    std::string env = "CC=";
2248
0
    if (!this->CCEnvironment.empty()) {
2249
0
      env += this->CCEnvironment;
2250
0
      cmSystemTools::PutEnv(env);
2251
0
    } else {
2252
0
      cmSystemTools::UnPutEnv(env);
2253
0
    }
2254
0
    env = "CXX=";
2255
0
    if (!this->CXXEnvironment.empty()) {
2256
0
      env += this->CXXEnvironment;
2257
0
      cmSystemTools::PutEnv(env);
2258
0
    } else {
2259
0
      cmSystemTools::UnPutEnv(env);
2260
0
    }
2261
0
  }
2262
2263
  // set the new
2264
0
  this->GlobalGenerator = std::move(gg);
2265
2266
  // set the global flag for unix style paths on cmSystemTools as soon as
2267
  // the generator is set.  This allows gmake to be used on windows.
2268
0
  cmSystemTools::SetForceUnixPaths(this->GlobalGenerator->GetForceUnixPaths());
2269
2270
  // Save the environment variables CXX and CC
2271
0
  if (!cmSystemTools::GetEnv("CXX", this->CXXEnvironment)) {
2272
0
    this->CXXEnvironment.clear();
2273
0
  }
2274
0
  if (!cmSystemTools::GetEnv("CC", this->CCEnvironment)) {
2275
0
    this->CCEnvironment.clear();
2276
0
  }
2277
0
}
2278
2279
int cmake::DoPreConfigureChecks()
2280
0
{
2281
  // Make sure the Source directory contains a CMakeLists.txt file.
2282
0
  std::string srcList =
2283
0
    cmStrCat(this->GetHomeDirectory(), '/', this->CMakeListName);
2284
0
  if (!cmSystemTools::FileExists(srcList)) {
2285
0
    std::ostringstream err;
2286
0
    if (cmSystemTools::FileIsDirectory(this->GetHomeDirectory())) {
2287
0
      err << "The source directory \"" << this->GetHomeDirectory()
2288
0
          << "\" does not appear to contain " << this->CMakeListName << ".\n";
2289
0
    } else if (cmSystemTools::FileExists(this->GetHomeDirectory())) {
2290
0
      err << "The source directory \"" << this->GetHomeDirectory()
2291
0
          << "\" is a file, not a directory.\n";
2292
0
    } else {
2293
0
      err << "The source directory \"" << this->GetHomeDirectory()
2294
0
          << "\" does not exist.\n";
2295
0
    }
2296
0
    err << "Specify --help for usage, or press the help button on the CMake "
2297
0
           "GUI.";
2298
0
    cmSystemTools::Error(err.str());
2299
0
    return -2;
2300
0
  }
2301
2302
  // do a sanity check on some values
2303
0
  if (cmValue dir =
2304
0
        this->State->GetInitializedCacheValue("CMAKE_HOME_DIRECTORY")) {
2305
0
    std::string cacheStart = cmStrCat(*dir, '/', this->CMakeListName);
2306
0
    if (!cmSystemTools::SameFile(cacheStart, srcList)) {
2307
0
      std::string message =
2308
0
        cmStrCat("The source \"", srcList, "\" does not match the source \"",
2309
0
                 cacheStart,
2310
0
                 "\" used to generate cache.  Re-run cmake with a different "
2311
0
                 "source directory.");
2312
0
      cmSystemTools::Error(message);
2313
0
      return -2;
2314
0
    }
2315
0
  } else {
2316
0
    return 0;
2317
0
  }
2318
0
  return 1;
2319
0
}
2320
struct SaveCacheEntry
2321
{
2322
  std::string key;
2323
  std::string value;
2324
  std::string help;
2325
  cmStateEnums::CacheEntryType type;
2326
};
2327
2328
int cmake::HandleDeleteCacheVariables(std::string const& var)
2329
0
{
2330
0
  cmList argsSplit{ var, cmList::EmptyElements::Yes };
2331
  // erase the property to avoid infinite recursion
2332
0
  this->State->SetGlobalProperty("__CMAKE_DELETE_CACHE_CHANGE_VARS_", "");
2333
0
  if (this->GetIsInTryCompile()) {
2334
0
    return 0;
2335
0
  }
2336
0
  std::vector<SaveCacheEntry> saved;
2337
0
  std::ostringstream warning;
2338
0
  warning
2339
0
    << "You have changed variables that require your cache to be deleted.\n"
2340
0
       "Configure will be re-run and you may have to reset some variables.\n"
2341
0
       "The following variables have changed:\n";
2342
0
  for (auto i = argsSplit.begin(); i != argsSplit.end(); ++i) {
2343
0
    SaveCacheEntry save;
2344
0
    save.key = *i;
2345
0
    warning << *i << "= ";
2346
0
    i++;
2347
0
    if (i != argsSplit.end()) {
2348
0
      save.value = *i;
2349
0
      warning << *i << '\n';
2350
0
    } else {
2351
0
      warning << '\n';
2352
0
      i -= 1;
2353
0
    }
2354
0
    cmValue existingValue = this->State->GetCacheEntryValue(save.key);
2355
0
    if (existingValue) {
2356
0
      save.type = this->State->GetCacheEntryType(save.key);
2357
0
      if (cmValue help =
2358
0
            this->State->GetCacheEntryProperty(save.key, "HELPSTRING")) {
2359
0
        save.help = *help;
2360
0
      }
2361
0
    } else {
2362
0
      save.type = cmStateEnums::CacheEntryType::UNINITIALIZED;
2363
0
    }
2364
0
    saved.push_back(std::move(save));
2365
0
  }
2366
2367
  // remove the cache
2368
0
  this->DeleteCache(this->GetHomeOutputDirectory());
2369
  // load the empty cache
2370
0
  this->LoadCache();
2371
  // restore the changed compilers
2372
0
  for (SaveCacheEntry const& i : saved) {
2373
0
    this->AddCacheEntry(i.key, i.value, i.help, i.type);
2374
0
  }
2375
0
  cmSystemTools::Message(warning.str());
2376
  // avoid reconfigure if there were errors
2377
0
  if (!cmSystemTools::GetErrorOccurredFlag()) {
2378
    // re-run configure
2379
0
    return this->Configure();
2380
0
  }
2381
0
  return 0;
2382
0
}
2383
2384
int cmake::Configure()
2385
0
{
2386
0
#if !defined(CMAKE_BOOTSTRAP)
2387
0
  auto profilingRAII = this->CreateProfilingEntry("project", "configure");
2388
0
#endif
2389
2390
  // We now need to harmonize the previous initial diagnostic state with any
2391
  // changes requested via command line options. This is a bit tricky, because
2392
  // we need to underlay what is specified by the cache beneath whatever state
2393
  // has been built from command line processing.
2394
2395
0
  cmDiagnosticAction deprecated = this->CurrentSnapshot.GetDiagnostic(
2396
0
    cmDiagnostics::CMD_DEPRECATED, cmDiagnostics::Undefined);
2397
0
  bool const deprecatedAlreadySet = (deprecated != cmDiagnostics::Undefined);
2398
2399
0
  if (cmValue cachedDiagnostics =
2400
0
        this->State->GetCacheEntryValue("CMAKE_DIAGNOSTIC_INIT")) {
2401
0
    for (std::string const& item : cmList{ cachedDiagnostics }) {
2402
0
      std::string::size_type n = item.find('=');
2403
0
      if (n != std::string::npos) {
2404
0
        cm::string_view v = item;
2405
0
        cm::optional<cmDiagnosticCategory> const& category =
2406
0
          cmDiagnostics::GetDiagnosticCategory(v.substr(0, n));
2407
0
        cm::optional<cmDiagnosticAction> const& action =
2408
0
          cmDiagnostics::GetDiagnosticAction(v.substr(n + 1));
2409
2410
0
        if (category && action) {
2411
          // Only use the cache if command-line options have not modified the
2412
          // diagnostic.
2413
0
          if (isDiagnosticSet(this->CurrentSnapshot, *category)) {
2414
0
            this->CurrentSnapshot.SetDiagnostic(*category, *action, false);
2415
0
          }
2416
0
        }
2417
0
      }
2418
0
    }
2419
0
  }
2420
2421
0
  cmValue cachedWarnDeprecated =
2422
0
    this->State->GetCacheEntryValue("CMAKE_WARN_DEPRECATED");
2423
0
  if (cachedWarnDeprecated) {
2424
0
    std::cerr << "The CMAKE_WARN_DEPRECATED variable is deprecated.  "
2425
0
                 "Use CMAKE_DIAGNOSTIC_INIT instead.\n"_s;
2426
0
    if (cachedWarnDeprecated.IsOn()) {
2427
0
      deprecated = cmDiagnostics::Warn;
2428
0
    } else {
2429
0
      deprecated = cmDiagnostics::Ignore;
2430
0
    }
2431
0
  }
2432
2433
0
  cmValue cachedErrorDeprecated =
2434
0
    this->State->GetCacheEntryValue("CMAKE_ERROR_DEPRECATED");
2435
0
  if (cachedErrorDeprecated) {
2436
0
    std::cerr << "The CMAKE_ERROR_DEPRECATED variable is deprecated.  "
2437
0
                 "Use CMAKE_DIAGNOSTIC_INIT instead.\n"_s;
2438
0
    if (cachedErrorDeprecated.IsOn()) {
2439
0
      deprecated = cmDiagnostics::SendError;
2440
0
    }
2441
0
  }
2442
2443
0
  if (!deprecatedAlreadySet && deprecated != cmDiagnostics::Undefined) {
2444
    // CMD_DEPRECATED was not set by command-line options, but was altered by
2445
    // one or both of CMAKE_{WARN,ERROR}_DEPRECATED.
2446
0
    this->CurrentSnapshot.SetDiagnostic(cmDiagnostics::CMD_DEPRECATED,
2447
0
                                        deprecated, false);
2448
0
  }
2449
2450
  // Now write the diagnostic state back to the cache.
2451
0
  cmList diagnostics;
2452
0
  for (unsigned i = 1; i < cmDiagnostics::CategoryCount; ++i) {
2453
0
    auto const category = static_cast<cmDiagnosticCategory>(i);
2454
0
    auto const action = this->CurrentSnapshot.GetDiagnostic(category);
2455
2456
0
    diagnostics.emplace_back(
2457
0
      cmStrCat(cmDiagnostics::GetCategoryString(category), '=',
2458
0
               cmDiagnostics::GetActionString(action)));
2459
2460
0
    if (category == cmDiagnostics::CMD_DEPRECATED) {
2461
      // Set deprecated CMAKE_{WARN,ERROR}_DEPRECATED, but only in the cache,
2462
      // and only if they were already set in the cache.
2463
0
      if (cachedWarnDeprecated) {
2464
0
        std::string const value =
2465
0
          (action >= cmDiagnostics::Warn ? "ON" : "OFF");
2466
0
        this->AddCacheEntry("CMAKE_WARN_DEPRECATED", value,
2467
0
                            "Deprecated.  Use CMAKE_DIAGNOSTIC_INIT instead.",
2468
0
                            cmStateEnums::INTERNAL);
2469
0
      }
2470
0
      if (cachedErrorDeprecated) {
2471
0
        std::string const value =
2472
0
          (action >= cmDiagnostics::SendError ? "ON" : "OFF");
2473
0
        this->AddCacheEntry("CMAKE_ERROR_DEPRECATED", value,
2474
0
                            "Deprecated.  Use CMAKE_DIAGNOSTIC_INIT instead.",
2475
0
                            cmStateEnums::INTERNAL);
2476
0
      }
2477
0
    }
2478
0
  }
2479
2480
0
  this->AddCacheEntry("CMAKE_DIAGNOSTIC_INIT", cmJoin(diagnostics, ";"_s),
2481
0
                      "Set initial state for CMake diagnostics; "
2482
0
                      "used to persist state set by command-line options "
2483
0
                      "across invocations.",
2484
0
                      cmStateEnums::INTERNAL);
2485
2486
0
  int ret = this->ActualConfigure();
2487
0
  cmValue delCacheVars =
2488
0
    this->State->GetGlobalProperty("__CMAKE_DELETE_CACHE_CHANGE_VARS_");
2489
0
  if (delCacheVars && !delCacheVars->empty()) {
2490
0
    return this->HandleDeleteCacheVariables(*delCacheVars);
2491
0
  }
2492
0
  return ret;
2493
0
}
2494
2495
int cmake::ActualConfigure()
2496
0
{
2497
  // Construct right now our path conversion table before it's too late:
2498
0
  this->CleanupCommandsAndMacros();
2499
2500
0
  cmSystemTools::RemoveADirectory(this->GetHomeOutputDirectory() +
2501
0
                                  "/CMakeFiles/CMakeScratch");
2502
2503
0
  std::string cmlNameCache =
2504
0
    this->State->GetInitializedCacheValue("CMAKE_LIST_FILE_NAME");
2505
0
  if (!cmlNameCache.empty() && !this->CMakeListName.empty() &&
2506
0
      cmlNameCache != this->CMakeListName) {
2507
0
    std::string message =
2508
0
      cmStrCat("CMakeLists filename : \"", this->CMakeListName,
2509
0
               "\"\nDoes not match the previous: \"", cmlNameCache,
2510
0
               "\"\nEither remove the CMakeCache.txt file and CMakeFiles "
2511
0
               "directory or choose a different binary directory.");
2512
0
    cmSystemTools::Error(message);
2513
0
    return -2;
2514
0
  }
2515
0
  if (this->CMakeListName.empty()) {
2516
0
    this->CMakeListName =
2517
0
      cmlNameCache.empty() ? "CMakeLists.txt" : cmlNameCache;
2518
0
  }
2519
0
  if (this->CMakeListName != "CMakeLists.txt") {
2520
0
    this->IssueMessage(
2521
0
      MessageType::WARNING,
2522
0
      "This project has been configured with a project file other than "
2523
0
      "CMakeLists.txt. This feature is intended for temporary use during "
2524
0
      "development and not for publication of a final product.");
2525
0
  }
2526
0
  this->AddCacheEntry("CMAKE_LIST_FILE_NAME", this->CMakeListName,
2527
0
                      "Name of CMakeLists files to read",
2528
0
                      cmStateEnums::INTERNAL);
2529
2530
0
  int res = this->DoPreConfigureChecks();
2531
0
  if (res < 0) {
2532
0
    return -2;
2533
0
  }
2534
0
  if (!res) {
2535
0
    this->AddCacheEntry(
2536
0
      "CMAKE_HOME_DIRECTORY", this->GetHomeDirectory(),
2537
0
      "Source directory with the top level CMakeLists.txt file for this "
2538
0
      "project",
2539
0
      cmStateEnums::INTERNAL);
2540
0
  }
2541
2542
  // We want to create the package redirects directory as early as possible,
2543
  // but not before pre-configure checks have passed. This ensures we get
2544
  // errors about inappropriate source/binary directories first.
2545
0
  auto const redirectsDir =
2546
0
    cmStrCat(this->GetHomeOutputDirectory(), "/CMakeFiles/pkgRedirects");
2547
0
  cmSystemTools::RemoveADirectory(redirectsDir);
2548
0
  if (!cmSystemTools::MakeDirectory(redirectsDir)) {
2549
0
    cmSystemTools::Error(
2550
0
      cmStrCat("Unable to (re)create the private pkgRedirects directory:\n  ",
2551
0
               redirectsDir,
2552
0
               "\n"
2553
0
               "This may be caused by not having read/write access to "
2554
0
               "the build directory.\n"
2555
0
               "Try specifying a location with read/write access like:\n"
2556
0
               "  cmake -B build\n"
2557
0
               "If using a CMake presets file, ensure that preset parameter\n"
2558
0
               "'binaryDir' expands to a writable directory.\n"));
2559
0
    return -1;
2560
0
  }
2561
0
  this->AddCacheEntry("CMAKE_FIND_PACKAGE_REDIRECTS_DIR", redirectsDir,
2562
0
                      "Value Computed by CMake.", cmStateEnums::STATIC);
2563
2564
  // no generator specified on the command line
2565
0
  if (!this->GlobalGenerator) {
2566
0
    cmValue genName = this->State->GetInitializedCacheValue("CMAKE_GENERATOR");
2567
0
    cmValue extraGenName =
2568
0
      this->State->GetInitializedCacheValue("CMAKE_EXTRA_GENERATOR");
2569
0
    if (genName) {
2570
0
      std::string fullName =
2571
0
        cmExternalMakefileProjectGenerator::CreateFullGeneratorName(
2572
0
          *genName, extraGenName ? *extraGenName : "");
2573
0
      this->GlobalGenerator = this->CreateGlobalGenerator(fullName);
2574
0
    }
2575
0
    if (this->GlobalGenerator) {
2576
      // set the global flag for unix style paths on cmSystemTools as
2577
      // soon as the generator is set.  This allows gmake to be used
2578
      // on windows.
2579
0
      cmSystemTools::SetForceUnixPaths(
2580
0
        this->GlobalGenerator->GetForceUnixPaths());
2581
0
    } else {
2582
0
      this->CreateDefaultGlobalGenerator();
2583
0
    }
2584
0
    if (!this->GlobalGenerator) {
2585
0
      cmSystemTools::Error("Could not create generator");
2586
0
      return -1;
2587
0
    }
2588
0
  }
2589
2590
0
  cmValue genName = this->State->GetInitializedCacheValue("CMAKE_GENERATOR");
2591
0
  if (genName) {
2592
0
    if (!this->GlobalGenerator->MatchesGeneratorName(*genName)) {
2593
0
      std::string message =
2594
0
        cmStrCat("Error: generator : ", this->GlobalGenerator->GetName(),
2595
0
                 "\n"
2596
0
                 "Does not match the generator used previously: ",
2597
0
                 *genName,
2598
0
                 "\n"
2599
0
                 "Either remove the CMakeCache.txt file and CMakeFiles "
2600
0
                 "directory or choose a different binary directory.");
2601
0
      cmSystemTools::Error(message);
2602
0
      return -2;
2603
0
    }
2604
0
  }
2605
0
  if (!genName) {
2606
0
    this->AddCacheEntry("CMAKE_GENERATOR", this->GlobalGenerator->GetName(),
2607
0
                        "Name of generator.", cmStateEnums::INTERNAL);
2608
0
    this->AddCacheEntry(
2609
0
      "CMAKE_EXTRA_GENERATOR", this->GlobalGenerator->GetExtraGeneratorName(),
2610
0
      "Name of external makefile project generator.", cmStateEnums::INTERNAL);
2611
2612
0
    if (!this->State->GetInitializedCacheValue("CMAKE_TOOLCHAIN_FILE")) {
2613
0
      std::string envToolchain;
2614
0
      if (cmSystemTools::GetEnv("CMAKE_TOOLCHAIN_FILE", envToolchain) &&
2615
0
          !envToolchain.empty()) {
2616
0
        this->AddCacheEntry("CMAKE_TOOLCHAIN_FILE", envToolchain,
2617
0
                            "The CMake toolchain file",
2618
0
                            cmStateEnums::FILEPATH);
2619
0
      }
2620
0
    }
2621
0
  }
2622
2623
0
  if (cmValue instance =
2624
0
        this->State->GetInitializedCacheValue("CMAKE_GENERATOR_INSTANCE")) {
2625
0
    if (this->GeneratorInstanceSet && this->GeneratorInstance != *instance) {
2626
0
      std::string message =
2627
0
        cmStrCat("Error: generator instance: ", this->GeneratorInstance,
2628
0
                 "\n"
2629
0
                 "Does not match the instance used previously: ",
2630
0
                 *instance,
2631
0
                 "\n"
2632
0
                 "Either remove the CMakeCache.txt file and CMakeFiles "
2633
0
                 "directory or choose a different binary directory.");
2634
0
      cmSystemTools::Error(message);
2635
0
      return -2;
2636
0
    }
2637
0
  } else {
2638
0
    this->AddCacheEntry("CMAKE_GENERATOR_INSTANCE", this->GeneratorInstance,
2639
0
                        "Generator instance identifier.",
2640
0
                        cmStateEnums::INTERNAL);
2641
0
  }
2642
2643
0
  if (cmValue platformName =
2644
0
        this->State->GetInitializedCacheValue("CMAKE_GENERATOR_PLATFORM")) {
2645
0
    if (this->GeneratorPlatformSet &&
2646
0
        this->GeneratorPlatform != *platformName) {
2647
0
      std::string message =
2648
0
        cmStrCat("Error: generator platform: ", this->GeneratorPlatform,
2649
0
                 "\n"
2650
0
                 "Does not match the platform used previously: ",
2651
0
                 *platformName,
2652
0
                 "\n"
2653
0
                 "Either remove the CMakeCache.txt file and CMakeFiles "
2654
0
                 "directory or choose a different binary directory.");
2655
0
      cmSystemTools::Error(message);
2656
0
      return -2;
2657
0
    }
2658
0
  } else {
2659
0
    this->AddCacheEntry("CMAKE_GENERATOR_PLATFORM", this->GeneratorPlatform,
2660
0
                        "Name of generator platform.", cmStateEnums::INTERNAL);
2661
0
  }
2662
2663
0
  if (cmValue tsName =
2664
0
        this->State->GetInitializedCacheValue("CMAKE_GENERATOR_TOOLSET")) {
2665
0
    if (this->GeneratorToolsetSet && this->GeneratorToolset != *tsName) {
2666
0
      std::string message =
2667
0
        cmStrCat("Error: generator toolset: ", this->GeneratorToolset,
2668
0
                 "\n"
2669
0
                 "Does not match the toolset used previously: ",
2670
0
                 *tsName,
2671
0
                 "\n"
2672
0
                 "Either remove the CMakeCache.txt file and CMakeFiles "
2673
0
                 "directory or choose a different binary directory.");
2674
0
      cmSystemTools::Error(message);
2675
0
      return -2;
2676
0
    }
2677
0
  } else {
2678
0
    this->AddCacheEntry("CMAKE_GENERATOR_TOOLSET", this->GeneratorToolset,
2679
0
                        "Name of generator toolset.", cmStateEnums::INTERNAL);
2680
0
  }
2681
2682
0
  if (!this->State->GetInitializedCacheValue(
2683
0
        "CMAKE_INTERMEDIATE_DIR_STRATEGY") &&
2684
0
      this->IntermediateDirStrategy) {
2685
0
    this->AddCacheEntry(
2686
0
      "CMAKE_INTERMEDIATE_DIR_STRATEGY", *this->IntermediateDirStrategy,
2687
0
      "Select the intermediate directory strategy", cmStateEnums::INTERNAL);
2688
0
  }
2689
0
  if (!this->State->GetInitializedCacheValue(
2690
0
        "CMAKE_AUTOGEN_INTERMEDIATE_DIR_STRATEGY") &&
2691
0
      this->AutogenIntermediateDirStrategy) {
2692
0
    this->AddCacheEntry(
2693
0
      "CMAKE_AUTOGEN_INTERMEDIATE_DIR_STRATEGY",
2694
0
      *this->AutogenIntermediateDirStrategy,
2695
0
      "Select the intermediate directory strategy for Autogen",
2696
0
      cmStateEnums::INTERNAL);
2697
0
  }
2698
2699
0
  if (!this->State->GetInitializedCacheValue("CMAKE_TEST_LAUNCHER")) {
2700
0
    cm::optional<std::string> testLauncher =
2701
0
      cmSystemTools::GetEnvVar("CMAKE_TEST_LAUNCHER");
2702
0
    if (testLauncher && !testLauncher->empty()) {
2703
0
      std::string message = "Test launcher to run tests executable.";
2704
0
      this->AddCacheEntry("CMAKE_TEST_LAUNCHER", *testLauncher, message,
2705
0
                          cmStateEnums::STRING);
2706
0
    }
2707
0
  }
2708
2709
0
  if (!this->State->GetInitializedCacheValue(
2710
0
        "CMAKE_CROSSCOMPILING_EMULATOR")) {
2711
0
    cm::optional<std::string> emulator =
2712
0
      cmSystemTools::GetEnvVar("CMAKE_CROSSCOMPILING_EMULATOR");
2713
0
    if (emulator && !emulator->empty()) {
2714
0
      std::string message =
2715
0
        "Emulator to run executables and tests when cross compiling.";
2716
0
      this->AddCacheEntry("CMAKE_CROSSCOMPILING_EMULATOR", *emulator, message,
2717
0
                          cmStateEnums::STRING);
2718
0
    }
2719
0
  }
2720
2721
0
  if (!this->State->GetInitializedCacheValue(
2722
0
        "CMAKE_DISABLE_PRECOMPILE_HEADERS")) {
2723
0
    cm::optional<std::string> disablePrecompileHeaders =
2724
0
      cmSystemTools::GetEnvVar("CMAKE_DISABLE_PRECOMPILE_HEADERS");
2725
0
    if (disablePrecompileHeaders && !disablePrecompileHeaders->empty()) {
2726
0
      std::string message =
2727
0
        "Default value for DISABLE_PRECOMPILE_HEADERS of targets.";
2728
0
      this->AddCacheEntry("CMAKE_DISABLE_PRECOMPILE_HEADERS",
2729
0
                          *disablePrecompileHeaders, message,
2730
0
                          cmStateEnums::BOOL);
2731
0
    }
2732
0
  }
2733
2734
  // reset any system configuration information, except for when we are
2735
  // InTryCompile. With TryCompile the system info is taken from the parent's
2736
  // info to save time
2737
0
  if (!this->GetIsInTryCompile()) {
2738
0
    this->GlobalGenerator->ClearEnabledLanguages();
2739
0
  }
2740
2741
0
#if !defined(CMAKE_BOOTSTRAP)
2742
0
  this->InitializeFileAPI();
2743
0
  this->FileAPI->ReadQueries();
2744
0
  this->InitializeInstrumentation();
2745
2746
0
  if (!this->GetIsInTryCompile()) {
2747
0
    this->TruncateOutputLog("CMakeConfigureLog.yaml");
2748
0
    this->ConfigureLog = cm::make_unique<cmConfigureLog>(
2749
0
      cmStrCat(this->GetHomeOutputDirectory(), "/CMakeFiles"_s),
2750
0
      this->FileAPI->GetConfigureLogVersions());
2751
0
    this->Instrumentation->ClearGeneratedQueries();
2752
0
    this->Instrumentation->CheckCDashVariable();
2753
0
  }
2754
0
#endif
2755
2756
  // actually do the configure
2757
0
  auto startTime = std::chrono::steady_clock::now();
2758
0
#if !defined(CMAKE_BOOTSTRAP)
2759
0
  if (this->Instrumentation->HasErrors()) {
2760
0
    return 1;
2761
0
  }
2762
0
  auto doConfigure = [this]() -> int {
2763
0
    this->GlobalGenerator->Configure();
2764
0
    return 0;
2765
0
  };
2766
0
  int ret = this->Instrumentation->InstrumentCommand(
2767
0
    "configure", this->cmdArgs, [doConfigure]() { return doConfigure(); },
2768
0
    cm::nullopt, cm::nullopt,
2769
0
    this->GetIsInTryCompile() ? cmInstrumentation::LoadQueriesAfter::No
2770
0
                              : cmInstrumentation::LoadQueriesAfter::Yes);
2771
0
  if (ret != 0) {
2772
0
    return ret;
2773
0
  }
2774
#else
2775
  this->GlobalGenerator->Configure();
2776
#endif
2777
0
  auto endTime = std::chrono::steady_clock::now();
2778
2779
  // configure result
2780
0
  if (this->State->GetRole() == cmState::Role::Project) {
2781
0
    std::ostringstream msg;
2782
0
    if (cmSystemTools::GetErrorOccurredFlag()) {
2783
0
      msg << "Configuring incomplete, errors occurred!";
2784
0
    } else {
2785
0
      auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
2786
0
        endTime - startTime);
2787
0
      msg << "Configuring done (" << std::fixed << std::setprecision(1)
2788
0
          << ms.count() / 1000.0L << "s)";
2789
0
    }
2790
0
    this->UpdateProgress(msg.str(), -1);
2791
0
  }
2792
2793
0
#if !defined(CMAKE_BOOTSTRAP)
2794
0
  this->ConfigureLog.reset();
2795
0
#endif
2796
2797
  // Before saving the cache
2798
  // if the project did not define one of the entries below, add them now
2799
  // so users can edit the values in the cache:
2800
2801
0
  auto const& mf = this->GlobalGenerator->GetMakefiles()[0];
2802
2803
0
  if (mf->IsOn("CTEST_USE_LAUNCHERS") &&
2804
0
      !this->State->GetGlobalProperty("RULE_LAUNCH_COMPILE")) {
2805
0
    this->IssueMessage(MessageType::FATAL_ERROR,
2806
0
                       "CTEST_USE_LAUNCHERS is enabled, but the "
2807
0
                       "RULE_LAUNCH_COMPILE global property is not defined.\n"
2808
0
                       "Did you forget to include(CTest) in the toplevel "
2809
0
                       "CMakeLists.txt ?");
2810
0
  }
2811
  // Setup launchers for instrumentation
2812
0
#if !defined(CMAKE_BOOTSTRAP)
2813
0
  if (this->Instrumentation->HasQuery()) {
2814
0
    std::string launcher;
2815
0
    if (mf->IsOn("CTEST_USE_LAUNCHERS")) {
2816
0
      launcher = cmStrCat('"', cmSystemTools::GetCTestCommand(),
2817
0
                          "\" --launch "
2818
0
                          "--current-build-dir <CMAKE_CURRENT_BINARY_DIR> "
2819
0
                          "--object-dir <TARGET_SUPPORT_DIR> ");
2820
0
    } else {
2821
0
      launcher =
2822
0
        cmStrCat('"', cmSystemTools::GetCTestCommand(), "\" --instrument ");
2823
0
    }
2824
0
    std::string common_args =
2825
0
      cmStrCat(" --target-name <TARGET_NAME> --config <CONFIG> --build-dir \"",
2826
0
               this->State->GetBinaryDirectory(), "\" ");
2827
0
    this->State->SetGlobalProperty(
2828
0
      "RULE_LAUNCH_COMPILE",
2829
0
      cmStrCat(
2830
0
        launcher, "--command-type compile", common_args,
2831
0
        "--output <OBJECT> --source <SOURCE> --language <LANGUAGE> -- "));
2832
0
    this->State->SetGlobalProperty(
2833
0
      "RULE_LAUNCH_LINK",
2834
0
      cmStrCat(
2835
0
        launcher, "--command-type link", common_args,
2836
0
        "--output <TARGET> --config <CONFIG> --language <LANGUAGE> -- "));
2837
0
    this->State->SetGlobalProperty(
2838
0
      "RULE_LAUNCH_CUSTOM",
2839
0
      cmStrCat(launcher, "--command-type custom", common_args,
2840
0
               "--output \"<OUTPUT>\" --role <ROLE> -- "));
2841
0
  }
2842
0
#endif
2843
2844
0
  this->State->SaveVerificationScript(this->GetHomeOutputDirectory(),
2845
0
                                      this->Messenger.get());
2846
0
  this->SaveCache(this->GetHomeOutputDirectory());
2847
0
  if (cmSystemTools::GetErrorOccurredFlag()) {
2848
0
#if !defined(CMAKE_BOOTSTRAP)
2849
0
    this->FileAPI->WriteReplies(cmFileAPI::IndexFor::FailedConfigure);
2850
0
#endif
2851
0
    return -1;
2852
0
  }
2853
0
  return 0;
2854
0
}
2855
2856
std::unique_ptr<cmGlobalGenerator> cmake::EvaluateDefaultGlobalGenerator()
2857
0
{
2858
0
  if (!this->EnvironmentGenerator.empty()) {
2859
0
    auto gen = this->CreateGlobalGenerator(this->EnvironmentGenerator);
2860
0
    if (!gen) {
2861
0
      cmSystemTools::Error("CMAKE_GENERATOR was set but the specified "
2862
0
                           "generator doesn't exist. Using CMake default.");
2863
0
    } else {
2864
0
      return gen;
2865
0
    }
2866
0
  }
2867
#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(CMAKE_BOOT_MINGW)
2868
  std::string found;
2869
  // Try to find the newest VS installed on the computer and
2870
  // use that as a default if -G is not specified
2871
  std::string const vsregBase = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\";
2872
  static char const* const vsVariants[] = {
2873
    /* clang-format needs this comment to break after the opening brace */
2874
    "VisualStudio\\", "VCExpress\\", "WDExpress\\"
2875
  };
2876
  struct VSVersionedGenerator
2877
  {
2878
    char const* MSVersion;
2879
    char const* GeneratorName;
2880
  };
2881
  static VSVersionedGenerator const vsGenerators[] = {
2882
    { "14.0", "Visual Studio 14 2015" }, //
2883
  };
2884
  static char const* const vsEntries[] = {
2885
    "\\Setup\\VC;ProductDir", //
2886
    ";InstallDir"             //
2887
  };
2888
  if (cmVSSetupAPIHelper(18).IsVSInstalled()) {
2889
    found = "Visual Studio 18 2026";
2890
  } else if (cmVSSetupAPIHelper(17).IsVSInstalled()) {
2891
    found = "Visual Studio 17 2022";
2892
  } else if (cmVSSetupAPIHelper(16).IsVSInstalled()) {
2893
    found = "Visual Studio 16 2019";
2894
  } else if (cmVSSetupAPIHelper(15).IsVSInstalled()) {
2895
    found = "Visual Studio 15 2017";
2896
  } else {
2897
    for (VSVersionedGenerator const* g = cm::cbegin(vsGenerators);
2898
         found.empty() && g != cm::cend(vsGenerators); ++g) {
2899
      for (char const* const* v = cm::cbegin(vsVariants);
2900
           found.empty() && v != cm::cend(vsVariants); ++v) {
2901
        for (char const* const* e = cm::cbegin(vsEntries);
2902
             found.empty() && e != cm::cend(vsEntries); ++e) {
2903
          std::string const reg = vsregBase + *v + g->MSVersion + *e;
2904
          std::string dir;
2905
          if (cmSystemTools::ReadRegistryValue(reg, dir,
2906
                                               cmSystemTools::KeyWOW64_32) &&
2907
              cmSystemTools::PathExists(dir)) {
2908
            found = g->GeneratorName;
2909
          }
2910
        }
2911
      }
2912
    }
2913
  }
2914
  auto gen = this->CreateGlobalGenerator(found);
2915
  if (!gen) {
2916
    gen = cm::make_unique<cmGlobalNMakeMakefileGenerator>(this);
2917
  }
2918
  return std::unique_ptr<cmGlobalGenerator>(std::move(gen));
2919
#elif defined(CMAKE_BOOTSTRAP_NINJA)
2920
  return std::unique_ptr<cmGlobalGenerator>(
2921
    cm::make_unique<cmGlobalNinjaGenerator>(this));
2922
#else
2923
0
  return std::unique_ptr<cmGlobalGenerator>(
2924
0
    cm::make_unique<cmGlobalUnixMakefileGenerator3>(this));
2925
0
#endif
2926
0
}
2927
2928
void cmake::CreateDefaultGlobalGenerator()
2929
0
{
2930
0
  auto gen = this->EvaluateDefaultGlobalGenerator();
2931
#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(CMAKE_BOOT_MINGW)
2932
  // This print could be unified for all platforms
2933
  std::cout << "-- Building for: " << gen->GetName() << '\n';
2934
#endif
2935
0
  this->SetGlobalGenerator(std::move(gen));
2936
0
}
2937
2938
void cmake::PreLoadCMakeFiles()
2939
0
{
2940
0
  std::vector<std::string> args;
2941
0
  std::string pre_load = this->GetHomeDirectory();
2942
0
  if (!pre_load.empty()) {
2943
0
    pre_load += "/PreLoad.cmake";
2944
0
    if (cmSystemTools::FileExists(pre_load)) {
2945
0
      this->ReadListFile(args, pre_load);
2946
0
    }
2947
0
  }
2948
0
  pre_load = this->GetHomeOutputDirectory();
2949
0
  if (!pre_load.empty()) {
2950
0
    pre_load += "/PreLoad.cmake";
2951
0
    if (cmSystemTools::FileExists(pre_load)) {
2952
0
      this->ReadListFile(args, pre_load);
2953
0
    }
2954
0
  }
2955
0
}
2956
2957
#ifdef CMake_ENABLE_DEBUGGER
2958
2959
bool cmake::StartDebuggerIfEnabled()
2960
1
{
2961
1
  if (!this->GetDebuggerOn()) {
2962
1
    return true;
2963
1
  }
2964
2965
0
  if (!DebugAdapter) {
2966
0
    if (this->GetDebuggerPipe().empty()) {
2967
0
      std::cerr
2968
0
        << "Error: --debugger-pipe must be set when debugging is enabled.\n";
2969
0
      return false;
2970
0
    }
2971
2972
0
    try {
2973
0
      DebugAdapter = std::make_shared<cmDebugger::cmDebuggerAdapter>(
2974
0
        std::make_shared<cmDebugger::cmDebuggerPipeConnection>(
2975
0
          this->GetDebuggerPipe()),
2976
0
        this->GetDebuggerDapLogFile());
2977
0
    } catch (std::runtime_error const& error) {
2978
0
      std::cerr << "Error: Failed to create debugger adapter.\n";
2979
0
      std::cerr << error.what() << "\n";
2980
0
      return false;
2981
0
    }
2982
0
    Messenger->SetDebuggerAdapter(DebugAdapter);
2983
0
  }
2984
2985
0
  return true;
2986
0
}
2987
2988
void cmake::StopDebuggerIfNeeded(int exitCode)
2989
0
{
2990
0
  if (!this->GetDebuggerOn()) {
2991
0
    return;
2992
0
  }
2993
2994
  // The debug adapter may have failed to start (e.g. invalid pipe path).
2995
0
  if (DebugAdapter) {
2996
0
    DebugAdapter->ReportExitCode(exitCode);
2997
0
    DebugAdapter.reset();
2998
0
  }
2999
0
}
3000
3001
#endif
3002
3003
void cmake::InitializeFileAPI()
3004
0
{
3005
0
#ifndef CMAKE_BOOTSTRAP
3006
0
  if (!this->FileAPI) {
3007
0
    this->FileAPI = cm::make_unique<cmFileAPI>(this);
3008
0
  }
3009
0
#endif
3010
0
}
3011
3012
void cmake::InitializeInstrumentation()
3013
0
{
3014
0
#ifndef CMAKE_BOOTSTRAP
3015
0
  if (!this->Instrumentation) {
3016
0
    this->Instrumentation = cm::make_unique<cmInstrumentation>(
3017
0
      this->State->GetBinaryDirectory(),
3018
0
      cmInstrumentation::LoadQueriesAfter::No);
3019
0
  }
3020
0
#endif
3021
0
}
3022
3023
// handle a command line invocation
3024
int cmake::Run(std::vector<std::string> const& args, bool noconfigure)
3025
35
{
3026
  // Process the arguments
3027
35
  this->SetArgs(args);
3028
35
  if (cmSystemTools::GetErrorOccurredFlag()) {
3029
34
    return -1;
3030
34
  }
3031
1
  if (this->State->GetRole() == cmState::Role::Help) {
3032
0
    return 0;
3033
0
  }
3034
3035
1
#ifndef CMAKE_BOOTSTRAP
3036
  // Configure the SARIF log for the current run
3037
1
  cmSarif::LogFileWriter sarifLogFileWriter(
3038
1
    this->GetMessenger()->GetSarifResultsLog());
3039
1
  if (!sarifLogFileWriter.ConfigureForCMakeRun(*this)) {
3040
0
    return -1;
3041
0
  }
3042
1
#endif
3043
3044
  // Log the trace format version to the desired output
3045
1
  if (this->GetTrace()) {
3046
0
    this->PrintTraceFormatVersion();
3047
0
  }
3048
3049
  // If we are given a stamp list file check if it is really out of date.
3050
1
  if (!this->CheckStampList.empty() &&
3051
0
      cmakeCheckStampList(this->CheckStampList)) {
3052
0
    return 0;
3053
0
  }
3054
3055
  // If we are given a stamp file check if it is really out of date.
3056
1
  if (!this->CheckStampFile.empty() &&
3057
0
      cmakeCheckStampFile(this->CheckStampFile)) {
3058
0
    return 0;
3059
0
  }
3060
3061
1
  if (this->State->GetRole() == cmState::Role::Project) {
3062
0
    if (this->FreshCache) {
3063
0
      this->DeleteCache(this->GetHomeOutputDirectory());
3064
0
    }
3065
    // load the cache
3066
0
    if (this->LoadCache() < 0) {
3067
0
      cmSystemTools::Error("Error executing cmake::LoadCache(). Aborting.\n");
3068
0
      return -1;
3069
0
    }
3070
0
#ifndef CMAKE_BOOTSTRAP
3071
    // If no SARIF file has been explicitly specified, use the default path
3072
0
    if (!this->SarifFileOutput) {
3073
      // If no output file is specified, use the default path
3074
      // Enable parent directory creation for the default path
3075
0
      sarifLogFileWriter.SetPath(cmStrCat(this->GetHomeOutputDirectory(), '/',
3076
0
                                          cmSarif::PROJECT_DEFAULT_SARIF_FILE),
3077
0
                                 true);
3078
0
    }
3079
0
#endif
3080
1
  } else {
3081
1
    if (this->FreshCache) {
3082
0
      cmSystemTools::Error("--fresh allowed only when configuring a project");
3083
0
      return -1;
3084
0
    }
3085
1
    this->AddCMakePaths();
3086
1
  }
3087
3088
1
#ifndef CMAKE_BOOTSTRAP
3089
1
  this->ProcessPresetVariables();
3090
1
  this->ProcessPresetEnvironment();
3091
1
#endif
3092
  // Add any cache args
3093
1
  if (!this->SetCacheArgs(args)) {
3094
0
    cmSystemTools::Error("Run 'cmake --help' for all supported options.");
3095
0
    return -1;
3096
0
  }
3097
1
#ifndef CMAKE_BOOTSTRAP
3098
1
  if (this->GetLogLevel() == Message::LogLevel::LOG_VERBOSE ||
3099
1
      this->GetLogLevel() == Message::LogLevel::LOG_DEBUG ||
3100
1
      this->GetLogLevel() == Message::LogLevel::LOG_TRACE) {
3101
0
    this->PrintPresetVariables();
3102
0
    this->PrintPresetEnvironment();
3103
0
  }
3104
1
#endif
3105
3106
  // In script mode we terminate after running the script.
3107
1
  if (this->State->GetRole() != cmState::Role::Project) {
3108
1
    if (cmSystemTools::GetErrorOccurredFlag()) {
3109
1
      return -1;
3110
1
    }
3111
0
    return this->HasScriptModeExitCode() ? this->GetScriptModeExitCode() : 0;
3112
1
  }
3113
3114
0
#ifndef CMAKE_BOOTSTRAP
3115
  // CMake only responds to the SARIF variable in normal mode
3116
0
  this->MarkCliAsUsed(cmSarif::PROJECT_SARIF_FILE_VARIABLE);
3117
0
#endif
3118
3119
  // If MAKEFLAGS are given in the environment, remove the environment
3120
  // variable.  This will prevent try-compile from succeeding when it
3121
  // should fail (if "-i" is an option).  We cannot simply test
3122
  // whether "-i" is given and remove it because some make programs
3123
  // encode the MAKEFLAGS variable in a strange way.
3124
0
  if (cmSystemTools::HasEnv("MAKEFLAGS")) {
3125
0
    cmSystemTools::PutEnv("MAKEFLAGS=");
3126
0
  }
3127
3128
0
  this->PreLoadCMakeFiles();
3129
3130
0
  if (noconfigure) {
3131
0
    return 0;
3132
0
  }
3133
3134
  // now run the global generate
3135
  // Check the state of the build system to see if we need to regenerate.
3136
0
  if (!this->CheckBuildSystem()) {
3137
0
    return 0;
3138
0
  }
3139
  // After generating fbuild.bff, FastBuild sees rebuild-bff as outdated since
3140
  // it hasn’t built the target yet. To make it a no-op for future runs, we
3141
  // trigger a dummy fbuild invocation that creates this marker file and runs
3142
  // CMake, marking rebuild-bff as up-to-date.
3143
0
  std::string const FBuildRestatFile =
3144
0
    cmStrCat(this->GetHomeOutputDirectory(), '/', FASTBUILD_RESTAT_FILE);
3145
0
  if (cmSystemTools::FileExists(FBuildRestatFile)) {
3146
0
    cmsys::ifstream restat(FBuildRestatFile.c_str(),
3147
0
                           std::ios::in | std::ios::binary);
3148
0
    std::string const file((std::istreambuf_iterator<char>(restat)),
3149
0
                           std::istreambuf_iterator<char>());
3150
    // On Windows can not delete file if it's still opened.
3151
0
    restat.close();
3152
0
    cmSystemTools::Touch(file, true);
3153
0
    cmSystemTools::RemoveFile(FBuildRestatFile);
3154
0
    return 0;
3155
0
  }
3156
3157
0
#ifdef CMake_ENABLE_DEBUGGER
3158
0
  if (!this->StartDebuggerIfEnabled()) {
3159
0
    return -1;
3160
0
  }
3161
0
#endif
3162
3163
0
  int ret = this->Configure();
3164
0
  if (ret) {
3165
#if defined(CMAKE_HAVE_VS_GENERATORS)
3166
    if (!this->VSSolutionFile.empty() && this->GlobalGenerator) {
3167
      // CMake is running to regenerate a Visual Studio build tree
3168
      // during a build from the VS IDE.  The build files cannot be
3169
      // regenerated, so we should stop the build.
3170
      cmSystemTools::Message("CMake Configure step failed.  "
3171
                             "Build files cannot be regenerated correctly.  "
3172
                             "Attempting to stop IDE build.");
3173
      cmGlobalVisualStudioGenerator& gg =
3174
        cm::static_reference_cast<cmGlobalVisualStudioGenerator>(
3175
          this->GlobalGenerator);
3176
      gg.CallVisualStudioMacro(cmGlobalVisualStudioGenerator::MacroStop,
3177
                               this->VSSolutionFile);
3178
    }
3179
#endif
3180
0
    return ret;
3181
0
  }
3182
0
  ret = this->Generate();
3183
0
  if (ret) {
3184
0
    cmSystemTools::Message("CMake Generate step failed.  "
3185
0
                           "Build files cannot be regenerated correctly.");
3186
0
    return ret;
3187
0
  }
3188
0
  std::string message = cmStrCat("Build files have been written to: ",
3189
0
                                 this->GetHomeOutputDirectory());
3190
0
  this->UpdateProgress(message, -1);
3191
0
  return ret;
3192
0
}
3193
3194
int cmake::Generate()
3195
0
{
3196
0
  if (!this->GlobalGenerator) {
3197
0
    return -1;
3198
0
  }
3199
3200
0
  auto startTime = std::chrono::steady_clock::now();
3201
0
#if !defined(CMAKE_BOOTSTRAP)
3202
0
  auto profilingRAII = this->CreateProfilingEntry("project", "generate");
3203
0
  auto doGenerate = [this]() -> int {
3204
0
    if (!this->GlobalGenerator->Compute()) {
3205
0
      this->FileAPI->WriteReplies(cmFileAPI::IndexFor::FailedCompute);
3206
0
      return -1;
3207
0
    }
3208
0
    this->GlobalGenerator->Generate();
3209
0
    if (this->Instrumentation->HasQuery()) {
3210
0
      this->Instrumentation->WriteCMakeContent(this->GlobalGenerator);
3211
0
    }
3212
0
    return 0;
3213
0
  };
3214
3215
0
  int ret = this->Instrumentation->InstrumentCommand(
3216
0
    "generate", this->cmdArgs, [doGenerate]() { return doGenerate(); });
3217
0
  if (ret != 0) {
3218
0
    return ret;
3219
0
  }
3220
#else
3221
  if (!this->GlobalGenerator->Compute()) {
3222
    return -1;
3223
  }
3224
  this->GlobalGenerator->Generate();
3225
#endif
3226
0
  auto endTime = std::chrono::steady_clock::now();
3227
0
  {
3228
0
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(endTime -
3229
0
                                                                    startTime);
3230
0
    std::ostringstream msg;
3231
0
    msg << "Generating done (" << std::fixed << std::setprecision(1)
3232
0
        << ms.count() / 1000.0L << "s)";
3233
0
    this->UpdateProgress(msg.str(), -1);
3234
0
  }
3235
0
  if (!this->GraphVizFile.empty()) {
3236
0
    std::cout << "Generate graphviz: " << this->GraphVizFile << '\n';
3237
0
    this->GenerateGraphViz(this->GraphVizFile);
3238
0
  }
3239
0
  this->RunCheckForUnusedVariables();
3240
0
  if (cmSystemTools::GetErrorOccurredFlag()) {
3241
0
#if !defined(CMAKE_BOOTSTRAP)
3242
0
    this->FileAPI->WriteReplies(cmFileAPI::IndexFor::FailedGenerate);
3243
0
#endif
3244
0
    return -1;
3245
0
  }
3246
  // Save the cache again after a successful Generate so that any internal
3247
  // variables created during Generate are saved. (Specifically target GUIDs
3248
  // for the Visual Studio and Xcode generators.)
3249
0
  this->SaveCache(this->GetHomeOutputDirectory());
3250
3251
0
#if !defined(CMAKE_BOOTSTRAP)
3252
0
  this->GlobalGenerator->WriteInstallJson();
3253
0
  this->FileAPI->WriteReplies(cmFileAPI::IndexFor::Success);
3254
0
  this->Instrumentation->CollectTimingData(
3255
0
    cmInstrumentationQuery::Hook::PostGenerate);
3256
0
#endif
3257
3258
0
  return 0;
3259
0
}
3260
3261
void cmake::AddCacheEntry(std::string const& key, cmValue value,
3262
                          cmValue helpString, int type)
3263
3
{
3264
3
  this->State->AddCacheEntry(key, value, helpString,
3265
3
                             static_cast<cmStateEnums::CacheEntryType>(type));
3266
3
  this->UnwatchUnusedCli(key);
3267
3
}
3268
3269
bool cmake::DoWriteGlobVerifyTarget() const
3270
0
{
3271
0
  return this->State->DoWriteGlobVerifyTarget();
3272
0
}
3273
3274
std::string const& cmake::GetGlobVerifyScript() const
3275
0
{
3276
0
  return this->State->GetGlobVerifyScript();
3277
0
}
3278
3279
std::string const& cmake::GetGlobVerifyStamp() const
3280
0
{
3281
0
  return this->State->GetGlobVerifyStamp();
3282
0
}
3283
3284
void cmake::AddGlobCacheEntry(cmGlobCacheEntry const& entry,
3285
                              std::string const& variable,
3286
                              cmListFileBacktrace const& backtrace)
3287
0
{
3288
0
  this->State->AddGlobCacheEntry(entry, variable, backtrace,
3289
0
                                 this->Messenger.get());
3290
0
}
3291
3292
std::vector<cmGlobCacheEntry> cmake::GetGlobCacheEntries() const
3293
0
{
3294
0
  return this->State->GetGlobCacheEntries();
3295
0
}
3296
3297
std::vector<std::string> cmake::GetAllExtensions() const
3298
0
{
3299
0
  std::vector<std::string> allExt = this->CLikeSourceFileExtensions.ordered;
3300
0
  allExt.insert(allExt.end(), this->HeaderFileExtensions.ordered.begin(),
3301
0
                this->HeaderFileExtensions.ordered.end());
3302
  // cuda extensions are also in SourceFileExtensions so we ignore it here
3303
0
  allExt.insert(allExt.end(), this->FortranFileExtensions.ordered.begin(),
3304
0
                this->FortranFileExtensions.ordered.end());
3305
0
  allExt.insert(allExt.end(), this->HipFileExtensions.ordered.begin(),
3306
0
                this->HipFileExtensions.ordered.end());
3307
0
  allExt.insert(allExt.end(), this->ISPCFileExtensions.ordered.begin(),
3308
0
                this->ISPCFileExtensions.ordered.end());
3309
0
  return allExt;
3310
0
}
3311
3312
std::string cmake::StripExtension(std::string const& file) const
3313
0
{
3314
0
  auto dotpos = file.rfind('.');
3315
0
  if (dotpos != std::string::npos) {
3316
#if defined(_WIN32) || defined(__APPLE__)
3317
    auto ext = cmSystemTools::LowerCase(file.substr(dotpos + 1));
3318
#else
3319
0
    auto ext = cm::string_view(file).substr(dotpos + 1);
3320
0
#endif
3321
0
    if (this->IsAKnownExtension(ext)) {
3322
0
      return file.substr(0, dotpos);
3323
0
    }
3324
0
  }
3325
0
  return file;
3326
0
}
3327
3328
cmValue cmake::GetCacheDefinition(std::string const& name) const
3329
0
{
3330
0
  return this->State->GetInitializedCacheValue(name);
3331
0
}
3332
3333
void cmake::AddScriptingCommands() const
3334
35
{
3335
35
  GetScriptingCommands(this->GetState());
3336
35
}
3337
3338
void cmake::AddProjectCommands() const
3339
0
{
3340
0
  GetProjectCommands(this->GetState());
3341
0
}
3342
3343
void cmake::AddDefaultGenerators()
3344
35
{
3345
#if defined(_WIN32) && !defined(__CYGWIN__)
3346
#  if !defined(CMAKE_BOOT_MINGW)
3347
  this->Generators.push_back(
3348
    cmGlobalVisualStudioVersionedGenerator::NewFactory18());
3349
  this->Generators.push_back(
3350
    cmGlobalVisualStudioVersionedGenerator::NewFactory17());
3351
  this->Generators.push_back(
3352
    cmGlobalVisualStudioVersionedGenerator::NewFactory16());
3353
  this->Generators.push_back(
3354
    cmGlobalVisualStudioVersionedGenerator::NewFactory15());
3355
  this->Generators.push_back(cmGlobalVisualStudio14Generator::NewFactory());
3356
  this->Generators.push_back(cmGlobalBorlandMakefileGenerator::NewFactory());
3357
  this->Generators.push_back(cmGlobalNMakeMakefileGenerator::NewFactory());
3358
  this->Generators.push_back(cmGlobalJOMMakefileGenerator::NewFactory());
3359
#  endif
3360
  this->Generators.push_back(cmGlobalMSYSMakefileGenerator::NewFactory());
3361
  this->Generators.push_back(cmGlobalMinGWMakefileGenerator::NewFactory());
3362
#endif
3363
35
#if !defined(CMAKE_BOOTSTRAP)
3364
35
#  if (defined(__linux__) && !defined(__ANDROID__)) || defined(_WIN32)
3365
35
  this->Generators.push_back(cmGlobalGhsMultiGenerator::NewFactory());
3366
35
#  endif
3367
35
  this->Generators.push_back(cmGlobalUnixMakefileGenerator3::NewFactory());
3368
35
  this->Generators.push_back(cmGlobalNinjaGenerator::NewFactory());
3369
35
  this->Generators.push_back(cmGlobalNinjaMultiGenerator::NewFactory());
3370
35
  this->Generators.push_back(cmGlobalFastbuildGenerator::NewFactory());
3371
#elif defined(CMAKE_BOOTSTRAP_NINJA)
3372
  this->Generators.push_back(cmGlobalNinjaGenerator::NewFactory());
3373
#elif defined(CMAKE_BOOTSTRAP_MAKEFILES)
3374
  this->Generators.push_back(cmGlobalUnixMakefileGenerator3::NewFactory());
3375
#endif
3376
35
#if defined(CMAKE_USE_WMAKE)
3377
35
  this->Generators.push_back(cmGlobalWatcomWMakeGenerator::NewFactory());
3378
35
#endif
3379
#ifdef CMAKE_USE_XCODE
3380
  this->Generators.push_back(cmGlobalXCodeGenerator::NewFactory());
3381
#endif
3382
35
}
3383
3384
bool cmake::ParseCacheEntry(std::string const& entry, std::string& var,
3385
                            std::string& value,
3386
                            cmStateEnums::CacheEntryType& type)
3387
0
{
3388
0
  return cmState::ParseCacheEntry(entry, var, value, type);
3389
0
}
3390
3391
int cmake::LoadCache()
3392
0
{
3393
  // could we not read the cache
3394
0
  if (!this->LoadCache(this->GetHomeOutputDirectory())) {
3395
    // if it does exist, but isn't readable then warn the user
3396
0
    std::string cacheFile =
3397
0
      cmStrCat(this->GetHomeOutputDirectory(), "/CMakeCache.txt");
3398
0
    if (cmSystemTools::FileExists(cacheFile)) {
3399
0
      cmSystemTools::Error(
3400
0
        "There is a CMakeCache.txt file for the current binary tree but "
3401
0
        "cmake does not have permission to read it. Please check the "
3402
0
        "permissions of the directory you are trying to run CMake on.");
3403
0
      return -1;
3404
0
    }
3405
0
  }
3406
3407
  // setup CMAKE_ROOT and CMAKE_COMMAND
3408
0
  if (!this->AddCMakePaths()) {
3409
0
    return -3;
3410
0
  }
3411
0
  return 0;
3412
0
}
3413
3414
bool cmake::LoadCache(std::string const& path)
3415
0
{
3416
0
  std::set<std::string> emptySet;
3417
0
  return this->LoadCache(path, true, emptySet, emptySet);
3418
0
}
3419
3420
bool cmake::LoadCache(std::string const& path, bool internal,
3421
                      std::set<std::string>& excludes,
3422
                      std::set<std::string>& includes)
3423
0
{
3424
0
  bool result = this->State->LoadCache(path, internal, excludes, includes);
3425
0
  static auto const entries = { "CMAKE_CACHE_MAJOR_VERSION",
3426
0
                                "CMAKE_CACHE_MINOR_VERSION" };
3427
0
  for (auto const& entry : entries) {
3428
0
    this->UnwatchUnusedCli(entry);
3429
0
  }
3430
0
  return result;
3431
0
}
3432
3433
bool cmake::SaveCache(std::string const& path)
3434
0
{
3435
0
  bool result = this->State->SaveCache(path, this->GetMessenger());
3436
0
  static auto const entries = { "CMAKE_CACHE_MAJOR_VERSION",
3437
0
                                "CMAKE_CACHE_MINOR_VERSION",
3438
0
                                "CMAKE_CACHE_PATCH_VERSION",
3439
0
                                "CMAKE_CACHEFILE_DIR" };
3440
0
  for (auto const& entry : entries) {
3441
0
    this->UnwatchUnusedCli(entry);
3442
0
  }
3443
0
  return result;
3444
0
}
3445
3446
bool cmake::DeleteCache(std::string const& path)
3447
0
{
3448
0
  return this->State->DeleteCache(path);
3449
0
}
3450
3451
void cmake::SetProgressCallback(ProgressCallbackType f)
3452
0
{
3453
0
  this->ProgressCallback = std::move(f);
3454
0
}
3455
3456
void cmake::UpdateProgress(std::string const& msg, float prog)
3457
0
{
3458
0
  if (this->ProgressCallback && !this->GetIsInTryCompile()) {
3459
0
    this->ProgressCallback(msg, prog);
3460
0
  }
3461
0
}
3462
3463
bool cmake::GetIsInTryCompile() const
3464
0
{
3465
0
  return this->State->GetIsTryCompile() == cmState::TryCompile::Yes;
3466
0
}
3467
3468
void cmake::AppendGlobalGeneratorsDocumentation(
3469
  std::vector<cmDocumentationEntry>& v)
3470
0
{
3471
0
  auto const defaultGenerator = this->EvaluateDefaultGlobalGenerator();
3472
0
  auto const defaultName = defaultGenerator->GetName();
3473
0
  auto foundDefaultOne = false;
3474
3475
0
  for (auto const& g : this->Generators) {
3476
0
    v.emplace_back(g->GetDocumentation());
3477
0
    if (!foundDefaultOne && cmHasPrefix(v.back().Name, defaultName)) {
3478
0
      v.back().CustomNamePrefix = '*';
3479
0
      foundDefaultOne = true;
3480
0
    }
3481
0
  }
3482
0
}
3483
3484
void cmake::AppendExtraGeneratorsDocumentation(
3485
  std::vector<cmDocumentationEntry>& v)
3486
0
{
3487
0
  for (cmExternalMakefileProjectGeneratorFactory* eg : this->ExtraGenerators) {
3488
0
    std::string const doc = eg->GetDocumentation();
3489
0
    std::string const name = eg->GetName();
3490
3491
    // Aliases:
3492
0
    for (std::string const& a : eg->Aliases) {
3493
0
      v.emplace_back(cmDocumentationEntry{ a, doc });
3494
0
    }
3495
3496
    // Full names:
3497
0
    for (std::string const& g : eg->GetSupportedGlobalGenerators()) {
3498
0
      v.emplace_back(cmDocumentationEntry{
3499
0
        cmExternalMakefileProjectGenerator::CreateFullGeneratorName(g, name),
3500
0
        doc });
3501
0
    }
3502
0
  }
3503
0
}
3504
3505
std::vector<cmDocumentationEntry> cmake::GetGeneratorsDocumentation()
3506
0
{
3507
0
  std::vector<cmDocumentationEntry> v;
3508
0
  this->AppendGlobalGeneratorsDocumentation(v);
3509
0
  this->AppendExtraGeneratorsDocumentation(v);
3510
0
  return v;
3511
0
}
3512
3513
void cmake::PrintGeneratorList()
3514
0
{
3515
0
#ifndef CMAKE_BOOTSTRAP
3516
0
  cmDocumentation doc;
3517
0
  auto generators = this->GetGeneratorsDocumentation();
3518
0
  doc.AppendSection("Generators", generators);
3519
0
  std::cerr << '\n';
3520
0
  doc.PrintDocumentation(cmDocumentation::ListGenerators, std::cerr);
3521
0
#endif
3522
0
}
3523
3524
int cmake::CheckBuildSystem()
3525
0
{
3526
  // We do not need to rerun CMake.  Check dependency integrity.
3527
0
  bool const verbose = isCMakeVerbose();
3528
3529
  // This method will check the integrity of the build system if the
3530
  // option was given on the command line.  It reads the given file to
3531
  // determine whether CMake should rerun.
3532
3533
  // If no file is provided for the check, we have to rerun.
3534
0
  if (this->CheckBuildSystemArgument.empty()) {
3535
0
    if (verbose) {
3536
0
      cmSystemTools::Stdout("Re-run cmake no build system arguments\n");
3537
0
    }
3538
0
    return 1;
3539
0
  }
3540
3541
  // If the file provided does not exist, we have to rerun.
3542
0
  if (!cmSystemTools::FileExists(this->CheckBuildSystemArgument)) {
3543
0
    if (verbose) {
3544
0
      std::ostringstream msg;
3545
0
      msg << "Re-run cmake missing file: " << this->CheckBuildSystemArgument
3546
0
          << '\n';
3547
0
      cmSystemTools::Stdout(msg.str());
3548
0
    }
3549
0
    return 1;
3550
0
  }
3551
3552
  // Read the rerun check file and use it to decide whether to do the
3553
  // global generate.
3554
  // Actually, all we need is the `set` command.
3555
0
  cmake cm(cmState::Role::Script);
3556
0
  cm.GetCurrentSnapshot().SetDefaultDefinitions();
3557
0
  cmGlobalGenerator gg(&cm);
3558
0
  cmMakefile mf(&gg, cm.GetCurrentSnapshot());
3559
0
  if (!mf.ReadListFile(this->CheckBuildSystemArgument) ||
3560
0
      cmSystemTools::GetErrorOccurredFlag()) {
3561
0
    if (verbose) {
3562
0
      std::ostringstream msg;
3563
0
      msg << "Re-run cmake error reading : " << this->CheckBuildSystemArgument
3564
0
          << '\n';
3565
0
      cmSystemTools::Stdout(msg.str());
3566
0
    }
3567
    // There was an error reading the file.  Just rerun.
3568
0
    return 1;
3569
0
  }
3570
3571
0
  if (this->ClearBuildSystem) {
3572
    // Get the generator used for this build system.
3573
0
    std::string genName = mf.GetSafeDefinition("CMAKE_DEPENDS_GENERATOR");
3574
0
    if (!cmNonempty(genName)) {
3575
0
      genName = "Unix Makefiles";
3576
0
    }
3577
3578
    // Create the generator and use it to clear the dependencies.
3579
0
    std::unique_ptr<cmGlobalGenerator> ggd =
3580
0
      this->CreateGlobalGenerator(genName);
3581
0
    if (ggd) {
3582
0
      cm.GetCurrentSnapshot().SetDefaultDefinitions();
3583
0
      cmMakefile mfd(ggd.get(), cm.GetCurrentSnapshot());
3584
0
      auto lgd = ggd->CreateLocalGenerator(&mfd);
3585
0
      lgd->ClearDependencies(&mfd, verbose);
3586
0
    }
3587
0
  }
3588
3589
  // If any byproduct of makefile generation is missing we must re-run.
3590
0
  cmList products{ mf.GetDefinition("CMAKE_MAKEFILE_PRODUCTS") };
3591
0
  for (auto const& p : products) {
3592
0
    if (!cmSystemTools::PathExists(p)) {
3593
0
      if (verbose) {
3594
0
        cmSystemTools::Stdout(
3595
0
          cmStrCat("Re-run cmake, missing byproduct: ", p, '\n'));
3596
0
      }
3597
0
      return 1;
3598
0
    }
3599
0
  }
3600
3601
  // Get the set of dependencies and outputs.
3602
0
  cmList depends{ mf.GetDefinition("CMAKE_MAKEFILE_DEPENDS") };
3603
0
  cmList outputs;
3604
0
  if (!depends.empty()) {
3605
0
    outputs.assign(mf.GetDefinition("CMAKE_MAKEFILE_OUTPUTS"));
3606
0
  }
3607
0
  if (depends.empty() || outputs.empty()) {
3608
    // Not enough information was provided to do the test.  Just rerun.
3609
0
    if (verbose) {
3610
0
      cmSystemTools::Stdout("Re-run cmake no CMAKE_MAKEFILE_DEPENDS "
3611
0
                            "or CMAKE_MAKEFILE_OUTPUTS :\n");
3612
0
    }
3613
0
    return 1;
3614
0
  }
3615
3616
  // Find the newest dependency.
3617
0
  auto dep = depends.begin();
3618
0
  std::string dep_newest = *dep++;
3619
0
  for (; dep != depends.end(); ++dep) {
3620
0
    int result = 0;
3621
0
    if (this->FileTimeCache->Compare(dep_newest, *dep, &result)) {
3622
0
      if (result < 0) {
3623
0
        dep_newest = *dep;
3624
0
      }
3625
0
    } else {
3626
0
      if (verbose) {
3627
0
        cmSystemTools::Stdout(
3628
0
          "Re-run cmake: build system dependency is missing\n");
3629
0
      }
3630
0
      return 1;
3631
0
    }
3632
0
  }
3633
3634
  // Find the oldest output.
3635
0
  auto out = outputs.begin();
3636
0
  std::string out_oldest = *out++;
3637
0
  for (; out != outputs.end(); ++out) {
3638
0
    int result = 0;
3639
0
    if (this->FileTimeCache->Compare(out_oldest, *out, &result)) {
3640
0
      if (result > 0) {
3641
0
        out_oldest = *out;
3642
0
      }
3643
0
    } else {
3644
0
      if (verbose) {
3645
0
        cmSystemTools::Stdout(
3646
0
          "Re-run cmake: build system output is missing\n");
3647
0
      }
3648
0
      return 1;
3649
0
    }
3650
0
  }
3651
3652
  // If any output is older than any dependency then rerun.
3653
0
  {
3654
0
    int result = 0;
3655
0
    if (!this->FileTimeCache->Compare(out_oldest, dep_newest, &result) ||
3656
0
        result < 0) {
3657
0
      if (verbose) {
3658
0
        std::ostringstream msg;
3659
0
        msg << "Re-run cmake file: " << out_oldest
3660
0
            << " older than: " << dep_newest << '\n';
3661
0
        cmSystemTools::Stdout(msg.str());
3662
0
      }
3663
0
      return 1;
3664
0
    }
3665
0
  }
3666
3667
  // No need to rerun.
3668
0
  return 0;
3669
0
}
3670
3671
void cmake::TruncateOutputLog(char const* fname)
3672
0
{
3673
0
  std::string fullPath = cmStrCat(this->GetHomeOutputDirectory(), '/', fname);
3674
0
  struct stat st;
3675
0
  if (::stat(fullPath.c_str(), &st)) {
3676
0
    return;
3677
0
  }
3678
0
  if (!this->State->GetInitializedCacheValue("CMAKE_CACHEFILE_DIR")) {
3679
0
    cmSystemTools::RemoveFile(fullPath);
3680
0
    return;
3681
0
  }
3682
0
  off_t fsize = st.st_size;
3683
0
  off_t const maxFileSize = 50 * 1024;
3684
0
  if (fsize < maxFileSize) {
3685
    // TODO: truncate file
3686
0
    return;
3687
0
  }
3688
0
}
3689
3690
void cmake::MarkCliAsUsed(std::string const& variable)
3691
0
{
3692
0
  this->UsedCliVariables[variable] = true;
3693
0
}
3694
3695
void cmake::GenerateGraphViz(std::string const& fileName) const
3696
0
{
3697
0
#ifndef CMAKE_BOOTSTRAP
3698
0
  cmGraphVizWriter gvWriter(fileName, this->GetGlobalGenerator());
3699
3700
0
  std::string settingsFile =
3701
0
    cmStrCat(this->GetHomeOutputDirectory(), "/CMakeGraphVizOptions.cmake");
3702
0
  std::string fallbackSettingsFile =
3703
0
    cmStrCat(this->GetHomeDirectory(), "/CMakeGraphVizOptions.cmake");
3704
3705
0
  gvWriter.ReadSettings(settingsFile, fallbackSettingsFile);
3706
3707
0
  gvWriter.Write();
3708
3709
0
#endif
3710
0
}
3711
3712
void cmake::SetProperty(std::string const& prop, cmValue value)
3713
0
{
3714
0
  this->State->SetGlobalProperty(prop, value);
3715
0
}
3716
3717
void cmake::AppendProperty(std::string const& prop, std::string const& value,
3718
                           bool asString)
3719
0
{
3720
0
  this->State->AppendGlobalProperty(prop, value, asString);
3721
0
}
3722
3723
cmValue cmake::GetProperty(std::string const& prop)
3724
0
{
3725
0
  return this->State->GetGlobalProperty(prop);
3726
0
}
3727
3728
bool cmake::GetPropertyAsBool(std::string const& prop)
3729
0
{
3730
0
  return this->State->GetGlobalPropertyAsBool(prop);
3731
0
}
3732
3733
cmInstalledFile* cmake::GetOrCreateInstalledFile(cmMakefile* mf,
3734
                                                 std::string const& name)
3735
0
{
3736
0
  auto i = this->InstalledFiles.find(name);
3737
3738
0
  if (i != this->InstalledFiles.end()) {
3739
0
    cmInstalledFile& file = i->second;
3740
0
    return &file;
3741
0
  }
3742
0
  cmInstalledFile& file = this->InstalledFiles[name];
3743
0
  file.SetName(mf, name);
3744
0
  return &file;
3745
0
}
3746
3747
cmInstalledFile const* cmake::GetInstalledFile(std::string const& name) const
3748
0
{
3749
0
  auto i = this->InstalledFiles.find(name);
3750
3751
0
  if (i != this->InstalledFiles.end()) {
3752
0
    cmInstalledFile const& file = i->second;
3753
0
    return &file;
3754
0
  }
3755
0
  return nullptr;
3756
0
}
3757
3758
int cmake::GetSystemInformation(std::vector<std::string>& args)
3759
0
{
3760
  // so create the directory
3761
0
  std::string resultFile;
3762
0
  std::string cwd = cmSystemTools::GetLogicalWorkingDirectory();
3763
0
  std::string destPath = cwd + "/__cmake_systeminformation";
3764
0
  cmSystemTools::RemoveADirectory(destPath);
3765
0
  if (!cmSystemTools::MakeDirectory(destPath)) {
3766
0
    std::cerr << "Error: --system-information must be run from a "
3767
0
                 "writable directory!\n";
3768
0
    return 1;
3769
0
  }
3770
3771
  // process the arguments
3772
0
  bool writeToStdout = true;
3773
0
  for (unsigned int i = 1; i < args.size(); ++i) {
3774
0
    std::string const& arg = args[i];
3775
0
    if (cmHasLiteralPrefix(arg, "-G")) {
3776
0
      std::string value = arg.substr(2);
3777
0
      if (value.empty()) {
3778
0
        ++i;
3779
0
        if (i >= args.size()) {
3780
0
          cmSystemTools::Error("No generator specified for -G");
3781
0
          this->PrintGeneratorList();
3782
0
          return -1;
3783
0
        }
3784
0
        value = args[i];
3785
0
      }
3786
0
      auto gen = this->CreateGlobalGenerator(value);
3787
0
      if (!gen) {
3788
0
        cmSystemTools::Error("Could not create named generator " + value);
3789
0
        this->PrintGeneratorList();
3790
0
      } else {
3791
0
        this->SetGlobalGenerator(std::move(gen));
3792
0
      }
3793
0
    }
3794
    // no option assume it is the output file
3795
0
    else {
3796
0
      if (!cmSystemTools::FileIsFullPath(arg)) {
3797
0
        resultFile = cmStrCat(cwd, '/');
3798
0
      }
3799
0
      resultFile += arg;
3800
0
      writeToStdout = false;
3801
0
    }
3802
0
  }
3803
3804
  // we have to find the module directory, so we can copy the files
3805
0
  this->AddCMakePaths();
3806
0
  std::string modulesPath =
3807
0
    cmStrCat(cmSystemTools::GetCMakeRoot(), "/Modules");
3808
0
  std::string inFile = cmStrCat(modulesPath, "/SystemInformation.cmake");
3809
0
  std::string outFile = cmStrCat(destPath, "/CMakeLists.txt");
3810
3811
  // Copy file
3812
0
  if (!cmsys::SystemTools::CopyFileAlways(inFile, outFile)) {
3813
0
    std::cerr << "Error copying file \"" << inFile << "\" to \"" << outFile
3814
0
              << "\".\n";
3815
0
    return 1;
3816
0
  }
3817
3818
  // do we write to a file or to stdout?
3819
0
  if (resultFile.empty()) {
3820
0
    resultFile = cmStrCat(cwd, "/__cmake_systeminformation/results.txt");
3821
0
  }
3822
3823
0
  {
3824
    // now run cmake on the CMakeLists file
3825
0
    cmWorkingDirectory workdir(destPath);
3826
0
    if (workdir.Failed()) {
3827
      // We created the directory and we were able to copy the CMakeLists.txt
3828
      // file to it, so we wouldn't expect to get here unless the default
3829
      // permissions are questionable or some other process has deleted the
3830
      // directory
3831
0
      std::cerr << workdir.GetError() << '\n';
3832
0
      return 1;
3833
0
    }
3834
0
    std::vector<std::string> args2;
3835
0
    args2.push_back(args[0]);
3836
0
    args2.push_back(destPath);
3837
0
    args2.push_back("-DRESULT_FILE=" + resultFile);
3838
0
    int res = this->Run(args2, false);
3839
3840
0
    if (res != 0) {
3841
0
      std::cerr << "Error: --system-information failed on internal CMake!\n";
3842
0
      return res;
3843
0
    }
3844
0
  }
3845
3846
  // echo results to stdout if needed
3847
0
  if (writeToStdout) {
3848
0
    FILE* fin = cmsys::SystemTools::Fopen(resultFile, "r");
3849
0
    if (fin) {
3850
0
      int const bufferSize = 4096;
3851
0
      char buffer[bufferSize];
3852
0
      size_t n;
3853
0
      while ((n = fread(buffer, 1, bufferSize, fin)) > 0) {
3854
0
        for (char* c = buffer; c < buffer + n; ++c) {
3855
0
          putc(*c, stdout);
3856
0
        }
3857
0
        fflush(stdout);
3858
0
      }
3859
0
      fclose(fin);
3860
0
    }
3861
0
  }
3862
3863
  // clean up the directory
3864
0
  cmSystemTools::RemoveADirectory(destPath);
3865
0
  return 0;
3866
0
}
3867
3868
void cmake::IssueMessage(MessageType t, std::string const& text,
3869
                         cmListFileBacktrace const& backtrace) const
3870
1
{
3871
1
  this->Messenger->IssueMessage(t, text, backtrace);
3872
1
}
3873
3874
void cmake::IssueDiagnostic(cmDiagnosticCategory category,
3875
                            std::string const& text,
3876
                            cmStateSnapshot const& state,
3877
                            cmListFileBacktrace const& backtrace) const
3878
0
{
3879
0
  this->Messenger->IssueDiagnostic(category, text, state, backtrace);
3880
0
}
3881
3882
std::vector<std::string> cmake::GetDebugConfigs()
3883
0
{
3884
0
  cmList configs;
3885
0
  if (cmValue config_list =
3886
0
        this->State->GetGlobalProperty("DEBUG_CONFIGURATIONS")) {
3887
    // Expand the specified list and convert to upper-case.
3888
0
    configs.assign(*config_list);
3889
0
    configs.transform(cmList::TransformAction::TOUPPER);
3890
0
  }
3891
  // If no configurations were specified, use a default list.
3892
0
  if (configs.empty()) {
3893
0
    configs.emplace_back("DEBUG");
3894
0
  }
3895
0
  return std::move(configs.data());
3896
0
}
3897
3898
int cmake::Build(cmBuildArgs buildArgs, std::vector<std::string> targets,
3899
                 std::vector<std::string> nativeOptions,
3900
                 cmBuildOptions& buildOptions, std::string const& presetName,
3901
                 bool listPresets, std::vector<std::string> const& args)
3902
0
{
3903
0
  buildArgs.timeout = cmDuration::zero();
3904
3905
0
#if !defined(CMAKE_BOOTSTRAP)
3906
0
  if (!presetName.empty() || listPresets) {
3907
    // If the binary directory was specified, use it to find
3908
    // the source directory so we can locate the presets file.
3909
0
    if (!buildArgs.binaryDir.empty() &&
3910
0
        this->SetDirectoriesFromFile(buildArgs.binaryDir)) {
3911
      // HomeDirectory is now the source directory (found in CMakeCache.txt)
3912
0
    } else {
3913
      // Otherwise we assume this command was called from the source directory.
3914
0
      this->SetHomeDirectory(cmSystemTools::GetLogicalWorkingDirectory());
3915
0
      this->SetHomeOutputDirectory(
3916
0
        cmSystemTools::GetLogicalWorkingDirectory());
3917
0
    }
3918
0
    cmCMakePresetsGraph settingsFile;
3919
0
    auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory());
3920
0
    if (result != true) {
3921
0
      cmSystemTools::Error(
3922
0
        cmStrCat("Could not read presets from ", this->GetHomeDirectory(),
3923
0
                 ":\n", settingsFile.parseState.GetErrorMessage()));
3924
0
      return 1;
3925
0
    }
3926
3927
0
    if (listPresets) {
3928
0
      settingsFile.PrintBuildPresetList();
3929
0
      return 0;
3930
0
    }
3931
3932
0
    auto resolveResult =
3933
0
      settingsFile.ResolvePreset(presetName, settingsFile.BuildPresets);
3934
0
    auto resolveError =
3935
0
      cmCMakePresetsGraph::FormatPresetError<cmCMakePresetsGraph::BuildPreset>(
3936
0
        resolveResult.StatusCode, resolveResult.ErrorPresetName,
3937
0
        this->GetHomeDirectory());
3938
0
    if (resolveError) {
3939
0
      cmSystemTools::Error(*resolveError);
3940
0
      settingsFile.PrintBuildPresetList();
3941
0
      return 1;
3942
0
    }
3943
0
    auto const* expandedPreset = resolveResult.Preset;
3944
3945
0
    auto configurePresetPair =
3946
0
      settingsFile.ConfigurePresets.find(expandedPreset->ConfigurePreset);
3947
0
    if (configurePresetPair == settingsFile.ConfigurePresets.end()) {
3948
0
      cmSystemTools::Error(cmStrCat("No such configure preset in ",
3949
0
                                    this->GetHomeDirectory(), ": \"",
3950
0
                                    expandedPreset->ConfigurePreset, '"'));
3951
0
      this->PrintPresetList(settingsFile);
3952
0
      return 1;
3953
0
    }
3954
3955
0
    if (configurePresetPair->second.Unexpanded.Hidden) {
3956
0
      cmSystemTools::Error(cmStrCat("Cannot use hidden configure preset in ",
3957
0
                                    this->GetHomeDirectory(), ": \"",
3958
0
                                    expandedPreset->ConfigurePreset, '"'));
3959
0
      this->PrintPresetList(settingsFile);
3960
0
      return 1;
3961
0
    }
3962
3963
0
    auto const& expandedConfigurePreset = configurePresetPair->second.Expanded;
3964
0
    if (!expandedConfigurePreset) {
3965
0
      cmSystemTools::Error(cmStrCat("Could not evaluate configure preset \"",
3966
0
                                    expandedPreset->ConfigurePreset,
3967
0
                                    "\": Invalid macro expansion"));
3968
0
      return 1;
3969
0
    }
3970
3971
0
    if (buildArgs.binaryDir.empty() &&
3972
0
        !expandedConfigurePreset->BinaryDir.empty()) {
3973
0
      buildArgs.binaryDir = expandedConfigurePreset->BinaryDir;
3974
0
    }
3975
3976
0
    this->UnprocessedPresetEnvironment = expandedPreset->Environment;
3977
0
    this->ProcessPresetEnvironment();
3978
3979
0
    if ((buildArgs.jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL ||
3980
0
         buildArgs.jobs == cmake::NO_BUILD_PARALLEL_LEVEL) &&
3981
0
        expandedPreset->Jobs) {
3982
0
      if (*expandedPreset->Jobs > static_cast<unsigned int>(INT_MAX)) {
3983
0
        cmSystemTools::Error(
3984
0
          "The build preset \"jobs\" value is too large.\n");
3985
0
        return 1;
3986
0
      }
3987
0
      buildArgs.jobs = *expandedPreset->Jobs;
3988
0
    }
3989
3990
0
    if (targets.empty()) {
3991
0
      targets.insert(targets.begin(), expandedPreset->Targets.begin(),
3992
0
                     expandedPreset->Targets.end());
3993
0
    }
3994
3995
0
    if (buildArgs.config.empty()) {
3996
0
      buildArgs.config = expandedPreset->Configuration;
3997
0
    }
3998
3999
0
    if (!buildOptions.Clean && expandedPreset->CleanFirst) {
4000
0
      buildOptions.Clean = *expandedPreset->CleanFirst;
4001
0
    }
4002
4003
0
    if (buildOptions.ResolveMode == PackageResolveMode::Default &&
4004
0
        expandedPreset->ResolvePackageReferences) {
4005
0
      buildOptions.ResolveMode = *expandedPreset->ResolvePackageReferences;
4006
0
    }
4007
4008
0
    if (!buildArgs.verbose && expandedPreset->Verbose) {
4009
0
      buildArgs.verbose = *expandedPreset->Verbose;
4010
0
    }
4011
4012
0
    if (nativeOptions.empty()) {
4013
0
      nativeOptions.insert(nativeOptions.begin(),
4014
0
                           expandedPreset->NativeToolOptions.begin(),
4015
0
                           expandedPreset->NativeToolOptions.end());
4016
0
    }
4017
0
  }
4018
0
#endif
4019
4020
0
  if (!cmSystemTools::FileIsDirectory(buildArgs.binaryDir)) {
4021
0
    std::cerr << "Error: " << buildArgs.binaryDir << " is not a directory\n";
4022
0
    return 1;
4023
0
  }
4024
4025
0
  std::string cachePath = FindCacheFile(buildArgs.binaryDir);
4026
0
  if (!this->LoadCache(cachePath)) {
4027
0
    std::cerr
4028
0
      << "Error: not a CMake build directory (missing CMakeCache.txt)\n";
4029
0
    return 1;
4030
0
  }
4031
0
  cmValue cachedGenerator = this->State->GetCacheEntryValue("CMAKE_GENERATOR");
4032
0
  if (!cachedGenerator) {
4033
0
    std::cerr << "Error: could not find CMAKE_GENERATOR in Cache\n";
4034
0
    return 1;
4035
0
  }
4036
0
  auto gen = this->CreateGlobalGenerator(*cachedGenerator);
4037
0
  if (!gen) {
4038
0
    std::cerr << "Error: could not create CMAKE_GENERATOR \""
4039
0
              << *cachedGenerator << "\"\n";
4040
0
    return 1;
4041
0
  }
4042
0
  this->SetGlobalGenerator(std::move(gen));
4043
0
  cmValue cachedGeneratorInstance =
4044
0
    this->State->GetCacheEntryValue("CMAKE_GENERATOR_INSTANCE");
4045
0
  if (cachedGeneratorInstance) {
4046
0
    cmMakefile mf(this->GetGlobalGenerator(), this->GetCurrentSnapshot());
4047
0
    if (!this->GlobalGenerator->SetGeneratorInstance(*cachedGeneratorInstance,
4048
0
                                                     &mf)) {
4049
0
      return 1;
4050
0
    }
4051
0
  }
4052
0
  cmValue cachedGeneratorPlatform =
4053
0
    this->State->GetCacheEntryValue("CMAKE_GENERATOR_PLATFORM");
4054
0
  if (cachedGeneratorPlatform) {
4055
0
    cmMakefile mf(this->GetGlobalGenerator(), this->GetCurrentSnapshot());
4056
0
    if (!this->GlobalGenerator->SetGeneratorPlatform(*cachedGeneratorPlatform,
4057
0
                                                     &mf)) {
4058
0
      return 1;
4059
0
    }
4060
0
  }
4061
0
  cmValue cachedGeneratorToolset =
4062
0
    this->State->GetCacheEntryValue("CMAKE_GENERATOR_TOOLSET");
4063
0
  if (cachedGeneratorToolset) {
4064
0
    cmMakefile mf(this->GetGlobalGenerator(), this->GetCurrentSnapshot());
4065
0
    if (!this->GlobalGenerator->SetGeneratorToolset(*cachedGeneratorToolset,
4066
0
                                                    true, &mf)) {
4067
0
      return 1;
4068
0
    }
4069
0
  }
4070
0
  cmValue cachedProjectName =
4071
0
    this->State->GetCacheEntryValue("CMAKE_PROJECT_NAME");
4072
0
  if (!cachedProjectName) {
4073
0
    std::cerr << "Error: could not find CMAKE_PROJECT_NAME in Cache\n";
4074
0
    return 1;
4075
0
  }
4076
0
  buildArgs.projectName = *cachedProjectName;
4077
4078
0
  if (this->State->GetCacheEntryValue("CMAKE_VERBOSE_MAKEFILE").IsOn()) {
4079
0
    buildArgs.verbose = true;
4080
0
  }
4081
4082
#ifdef CMAKE_HAVE_VS_GENERATORS
4083
  // For VS generators, explicitly check if regeneration is necessary before
4084
  // actually starting the build. If not done separately from the build
4085
  // itself, there is the risk of building an out-of-date solution file due
4086
  // to limitations of the underlying build system.
4087
  std::string const stampList =
4088
    cmStrCat(cachePath, "/CMakeFiles/",
4089
             cmGlobalVisualStudio14Generator::GetGenerateStampList());
4090
4091
  // Note that the stampList file only exists for VS generators.
4092
  if (cmSystemTools::FileExists(stampList) &&
4093
      !cmakeCheckStampList(stampList)) {
4094
    // Upgrade cmake role from --build to reconfigure the project.
4095
    this->State->SetRoleToProjectForCMakeBuildVsReconfigure();
4096
    this->AddScriptingCommands();
4097
    this->AddProjectCommands();
4098
4099
    // Correctly initialize the home (=source) and home output (=binary)
4100
    // directories, which is required for running the generation step.
4101
    this->SetDirectoriesFromFile(cachePath);
4102
4103
    int ret = this->Configure();
4104
    if (ret) {
4105
      cmSystemTools::Message("CMake Configure step failed.  "
4106
                             "Build files cannot be regenerated correctly.");
4107
      return ret;
4108
    }
4109
    ret = this->Generate();
4110
    if (ret) {
4111
      cmSystemTools::Message("CMake Generate step failed.  "
4112
                             "Build files cannot be regenerated correctly.");
4113
      return ret;
4114
    }
4115
    std::string message = cmStrCat("Build files have been written to: ",
4116
                                   this->GetHomeOutputDirectory());
4117
    this->UpdateProgress(message, -1);
4118
  }
4119
#endif
4120
4121
0
  if (!this->GlobalGenerator->ReadCacheEntriesForBuild(*this->State)) {
4122
0
    return 1;
4123
0
  }
4124
4125
0
#if !defined(CMAKE_BOOTSTRAP)
4126
0
  cmInstrumentation instrumentation(buildArgs.binaryDir);
4127
0
  if (instrumentation.HasErrors()) {
4128
0
    return 1;
4129
0
  }
4130
0
  instrumentation.CollectTimingData(
4131
0
    cmInstrumentationQuery::Hook::PreCMakeBuild);
4132
0
#endif
4133
4134
0
  this->GlobalGenerator->PrintBuildCommandAdvice(std::cerr, buildArgs.jobs);
4135
0
  std::stringstream ostr;
4136
  // `cmGlobalGenerator::Build` logs metadata about what directory and commands
4137
  // are being executed to the `output` parameter. If CMake is verbose, print
4138
  // this out.
4139
0
  std::ostream& verbose_ostr = buildArgs.verbose ? std::cout : ostr;
4140
0
  auto doBuild = [this, targets, &verbose_ostr, buildOptions, buildArgs,
4141
0
                  nativeOptions]() -> int {
4142
0
    return this->GlobalGenerator->Build(
4143
0
      buildArgs, targets, verbose_ostr, "", buildArgs.config, buildOptions,
4144
0
      buildArgs.timeout, cmSystemTools::OUTPUT_PASSTHROUGH, nativeOptions);
4145
0
  };
4146
4147
0
#if !defined(CMAKE_BOOTSTRAP)
4148
  // Block the instrumentation build daemon from spawning during this build.
4149
  // This lock will be released when the process exits at the end of the build.
4150
0
  instrumentation.LockBuildDaemon();
4151
0
  int buildresult =
4152
0
    instrumentation.InstrumentCommand("cmakeBuild", args, doBuild);
4153
0
  instrumentation.CollectTimingData(
4154
0
    cmInstrumentationQuery::Hook::PostCMakeBuild);
4155
#else
4156
  int buildresult = doBuild();
4157
#endif
4158
4159
0
  return buildresult;
4160
0
}
4161
4162
bool cmake::Open(std::string const& dir, DryRun dryRun)
4163
0
{
4164
0
  if (!cmSystemTools::FileIsDirectory(dir)) {
4165
0
    if (dryRun == DryRun::No) {
4166
0
      std::cerr << "Error: " << dir << " is not a directory\n";
4167
0
    }
4168
0
    return false;
4169
0
  }
4170
4171
0
  std::string cachePath = FindCacheFile(dir);
4172
0
  if (!this->LoadCache(cachePath)) {
4173
0
    std::cerr
4174
0
      << "Error: not a CMake build directory (missing CMakeCache.txt)\n";
4175
0
    return false;
4176
0
  }
4177
0
  cmValue genName = this->State->GetCacheEntryValue("CMAKE_GENERATOR");
4178
0
  if (!genName) {
4179
0
    std::cerr << "Error: could not find CMAKE_GENERATOR in Cache\n";
4180
0
    return false;
4181
0
  }
4182
0
  cmValue extraGenName =
4183
0
    this->State->GetInitializedCacheValue("CMAKE_EXTRA_GENERATOR");
4184
0
  std::string fullName =
4185
0
    cmExternalMakefileProjectGenerator::CreateFullGeneratorName(
4186
0
      *genName, extraGenName ? *extraGenName : "");
4187
4188
0
  std::unique_ptr<cmGlobalGenerator> gen =
4189
0
    this->CreateGlobalGenerator(fullName);
4190
0
  if (!gen) {
4191
0
    std::cerr << "Error: could not create CMAKE_GENERATOR \"" << fullName
4192
0
              << "\"\n";
4193
0
    return false;
4194
0
  }
4195
4196
0
  cmValue cachedProjectName =
4197
0
    this->State->GetCacheEntryValue("CMAKE_PROJECT_NAME");
4198
0
  if (!cachedProjectName) {
4199
0
    std::cerr << "Error: could not find CMAKE_PROJECT_NAME in Cache\n";
4200
0
    return false;
4201
0
  }
4202
4203
0
  return gen->Open(dir, *cachedProjectName, dryRun == DryRun::Yes);
4204
0
}
4205
4206
#if !defined(CMAKE_BOOTSTRAP)
4207
template <typename T>
4208
T const* cmake::FindPresetForWorkflow(
4209
  cm::static_string_view type,
4210
  std::map<std::string, cmCMakePresetsGraph::PresetPair<T>> const& presets,
4211
  cmCMakePresetsGraph::WorkflowPreset::WorkflowStep const& step)
4212
0
{
4213
0
  auto it = presets.find(step.PresetName);
4214
0
  if (it == presets.end()) {
4215
0
    cmSystemTools::Error(cmStrCat("No such ", type, " preset in ",
4216
0
                                  this->GetHomeDirectory(), ": \"",
4217
0
                                  step.PresetName, '"'));
4218
0
    return nullptr;
4219
0
  }
4220
4221
0
  if (it->second.Unexpanded.Hidden) {
4222
0
    cmSystemTools::Error(cmStrCat("Cannot use hidden ", type, " preset in ",
4223
0
                                  this->GetHomeDirectory(), ": \"",
4224
0
                                  step.PresetName, '"'));
4225
0
    return nullptr;
4226
0
  }
4227
4228
0
  if (!it->second.Expanded) {
4229
0
    cmSystemTools::Error(cmStrCat("Could not evaluate ", type, " preset \"",
4230
0
                                  step.PresetName,
4231
0
                                  "\": Invalid macro expansion"));
4232
0
    return nullptr;
4233
0
  }
4234
4235
0
  if (!it->second.Expanded->ConditionResult) {
4236
0
    cmSystemTools::Error(cmStrCat("Cannot use disabled ", type, " preset in ",
4237
0
                                  this->GetHomeDirectory(), ": \"",
4238
0
                                  step.PresetName, '"'));
4239
0
    return nullptr;
4240
0
  }
4241
4242
0
  return &*it->second.Expanded;
4243
0
}
Unexecuted instantiation: cmCMakePresetsGraph::ConfigurePreset const* cmake::FindPresetForWorkflow<cmCMakePresetsGraph::ConfigurePreset>(cm::static_string_view, std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, cmCMakePresetsGraph::PresetPair<cmCMakePresetsGraph::ConfigurePreset>, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, cmCMakePresetsGraph::PresetPair<cmCMakePresetsGraph::ConfigurePreset> > > > const&, cmCMakePresetsGraph::WorkflowPreset::WorkflowStep const&)
Unexecuted instantiation: cmCMakePresetsGraph::BuildPreset const* cmake::FindPresetForWorkflow<cmCMakePresetsGraph::BuildPreset>(cm::static_string_view, std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, cmCMakePresetsGraph::PresetPair<cmCMakePresetsGraph::BuildPreset>, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, cmCMakePresetsGraph::PresetPair<cmCMakePresetsGraph::BuildPreset> > > > const&, cmCMakePresetsGraph::WorkflowPreset::WorkflowStep const&)
Unexecuted instantiation: cmCMakePresetsGraph::TestPreset const* cmake::FindPresetForWorkflow<cmCMakePresetsGraph::TestPreset>(cm::static_string_view, std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, cmCMakePresetsGraph::PresetPair<cmCMakePresetsGraph::TestPreset>, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, cmCMakePresetsGraph::PresetPair<cmCMakePresetsGraph::TestPreset> > > > const&, cmCMakePresetsGraph::WorkflowPreset::WorkflowStep const&)
Unexecuted instantiation: cmCMakePresetsGraph::PackagePreset const* cmake::FindPresetForWorkflow<cmCMakePresetsGraph::PackagePreset>(cm::static_string_view, std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, cmCMakePresetsGraph::PresetPair<cmCMakePresetsGraph::PackagePreset>, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, cmCMakePresetsGraph::PresetPair<cmCMakePresetsGraph::PackagePreset> > > > const&, cmCMakePresetsGraph::WorkflowPreset::WorkflowStep const&)
4244
4245
namespace {
4246
4247
std::function<cmUVProcessChain::Status()> buildWorkflowStep(
4248
  std::vector<std::string> const& args)
4249
0
{
4250
0
  cmUVProcessChainBuilder builder;
4251
0
  builder.AddCommand(args)
4252
0
    .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, stdout)
4253
0
    .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, stderr);
4254
0
  return [builder]() -> cmUVProcessChain::Status {
4255
0
    auto chain = builder.Start();
4256
0
    chain.Wait();
4257
0
    return chain.GetStatus(0);
4258
0
  };
4259
0
}
4260
4261
}
4262
#endif
4263
4264
int cmake::Workflow(std::string const& presetName,
4265
                    WorkflowListPresets listPresets, WorkflowFresh fresh)
4266
0
{
4267
0
  int exitStatus = 0;
4268
0
#ifndef CMAKE_BOOTSTRAP
4269
0
  this->SetHomeDirectory(cmSystemTools::GetLogicalWorkingDirectory());
4270
0
  this->SetHomeOutputDirectory(cmSystemTools::GetLogicalWorkingDirectory());
4271
4272
0
  cmCMakePresetsGraph settingsFile;
4273
0
  auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory());
4274
0
  if (result != true) {
4275
0
    cmSystemTools::Error(cmStrCat("Could not read presets from ",
4276
0
                                  this->GetHomeDirectory(), ":\n",
4277
0
                                  settingsFile.parseState.GetErrorMessage()));
4278
0
    return 1;
4279
0
  }
4280
4281
0
  if (listPresets == WorkflowListPresets::Yes) {
4282
0
    settingsFile.PrintWorkflowPresetList();
4283
0
    return 0;
4284
0
  }
4285
4286
0
  auto presetPair = settingsFile.WorkflowPresets.find(presetName);
4287
0
  if (presetPair == settingsFile.WorkflowPresets.end()) {
4288
0
    cmSystemTools::Error(cmStrCat("No such workflow preset in ",
4289
0
                                  this->GetHomeDirectory(), ": \"", presetName,
4290
0
                                  '"'));
4291
0
    settingsFile.PrintWorkflowPresetList();
4292
0
    return 1;
4293
0
  }
4294
4295
0
  if (presetPair->second.Unexpanded.Hidden) {
4296
0
    cmSystemTools::Error(cmStrCat("Cannot use hidden workflow preset in ",
4297
0
                                  this->GetHomeDirectory(), ": \"", presetName,
4298
0
                                  '"'));
4299
0
    settingsFile.PrintWorkflowPresetList();
4300
0
    return 1;
4301
0
  }
4302
4303
0
  auto const& expandedPreset = presetPair->second.Expanded;
4304
0
  if (!expandedPreset) {
4305
0
    cmSystemTools::Error(cmStrCat("Could not evaluate workflow preset \"",
4306
0
                                  presetName, "\": Invalid macro expansion"));
4307
0
    settingsFile.PrintWorkflowPresetList();
4308
0
    return 1;
4309
0
  }
4310
4311
0
  if (!expandedPreset->ConditionResult) {
4312
0
    cmSystemTools::Error(cmStrCat("Cannot use disabled workflow preset in ",
4313
0
                                  this->GetHomeDirectory(), ": \"", presetName,
4314
0
                                  '"'));
4315
0
    settingsFile.PrintWorkflowPresetList();
4316
0
    return 1;
4317
0
  }
4318
4319
0
  struct CalculatedStep
4320
0
  {
4321
0
    int StepNumber;
4322
0
    cm::static_string_view Type;
4323
0
    std::string Name;
4324
0
    std::function<cmUVProcessChain::Status()> Action;
4325
4326
0
    CalculatedStep(int stepNumber, cm::static_string_view type,
4327
0
                   std::string name,
4328
0
                   std::function<cmUVProcessChain::Status()> action)
4329
0
      : StepNumber(stepNumber)
4330
0
      , Type(type)
4331
0
      , Name(std::move(name))
4332
0
      , Action(std::move(action))
4333
0
    {
4334
0
    }
4335
0
  };
4336
4337
0
  std::vector<CalculatedStep> steps;
4338
0
  steps.reserve(expandedPreset->Steps.size());
4339
0
  int stepNumber = 1;
4340
0
  cmCMakePresetsGraph::ConfigurePreset const* configurePreset = {};
4341
0
  for (auto const& step : expandedPreset->Steps) {
4342
0
    switch (step.PresetType) {
4343
0
      case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::
4344
0
        Configure: {
4345
0
        configurePreset = this->FindPresetForWorkflow(
4346
0
          "configure"_s, settingsFile.ConfigurePresets, step);
4347
0
        if (!configurePreset) {
4348
0
          return 1;
4349
0
        }
4350
0
        std::vector<std::string> args{ cmSystemTools::GetCMakeCommand(),
4351
0
                                       "--preset", step.PresetName };
4352
0
        if (fresh == WorkflowFresh::Yes) {
4353
0
          args.emplace_back("--fresh");
4354
0
        }
4355
0
        steps.emplace_back(stepNumber, "configure"_s, step.PresetName,
4356
0
                           buildWorkflowStep(args));
4357
0
      } break;
4358
0
      case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::Build: {
4359
0
        auto const* buildPreset = this->FindPresetForWorkflow(
4360
0
          "build"_s, settingsFile.BuildPresets, step);
4361
0
        if (!buildPreset) {
4362
0
          return 1;
4363
0
        }
4364
0
        steps.emplace_back(
4365
0
          stepNumber, "build"_s, step.PresetName,
4366
0
          buildWorkflowStep({ cmSystemTools::GetCMakeCommand(), "--build",
4367
0
                              "--preset", step.PresetName }));
4368
0
      } break;
4369
0
      case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::Test: {
4370
0
        auto const* testPreset = this->FindPresetForWorkflow(
4371
0
          "test"_s, settingsFile.TestPresets, step);
4372
0
        if (!testPreset) {
4373
0
          return 1;
4374
0
        }
4375
0
        steps.emplace_back(
4376
0
          stepNumber, "test"_s, step.PresetName,
4377
0
          buildWorkflowStep({ cmSystemTools::GetCTestCommand(), "--preset",
4378
0
                              step.PresetName }));
4379
0
      } break;
4380
0
      case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::Package: {
4381
0
        auto const* packagePreset = this->FindPresetForWorkflow(
4382
0
          "package"_s, settingsFile.PackagePresets, step);
4383
0
        if (!packagePreset) {
4384
0
          return 1;
4385
0
        }
4386
0
        steps.emplace_back(
4387
0
          stepNumber, "package"_s, step.PresetName,
4388
0
          buildWorkflowStep({ cmSystemTools::GetCPackCommand(), "--preset",
4389
0
                              step.PresetName }));
4390
0
      } break;
4391
0
    }
4392
0
    stepNumber++;
4393
0
  }
4394
4395
0
  bool first = true;
4396
0
  for (auto const& step : steps) {
4397
0
    if (!first) {
4398
0
      std::cout << "\n";
4399
0
    }
4400
0
    std::cout << "Executing workflow step " << step.StepNumber << " of "
4401
0
              << steps.size() << ": " << step.Type << " preset \"" << step.Name
4402
0
              << "\"\n\n"
4403
0
              << std::flush;
4404
0
    cmUVProcessChain::Status const status = step.Action();
4405
0
    if (status.ExitStatus != 0) {
4406
0
      exitStatus = static_cast<int>(status.ExitStatus);
4407
0
      break;
4408
0
    }
4409
0
    auto const codeReasonPair = status.GetException();
4410
0
    if (codeReasonPair.first != cmUVProcessChain::ExceptionCode::None) {
4411
0
      std::cout << "Step command ended abnormally: " << codeReasonPair.second
4412
0
                << std::endl;
4413
0
      exitStatus =
4414
0
        status.SpawnResult != 0 ? status.SpawnResult : status.TermSignal;
4415
0
      break;
4416
0
    }
4417
0
    first = false;
4418
0
  }
4419
0
  if (configurePreset) {
4420
0
    cmInstrumentation instrumentation(configurePreset->BinaryDir);
4421
0
    instrumentation.CollectTimingData(
4422
0
      cmInstrumentationQuery::Hook::PostCMakeWorkflow);
4423
0
  }
4424
0
#endif
4425
4426
0
  return exitStatus;
4427
0
}
4428
4429
void cmake::WatchUnusedCli(std::string const& var)
4430
0
{
4431
0
#ifndef CMAKE_BOOTSTRAP
4432
0
  this->VariableWatch->AddWatch(var, cmWarnUnusedCliWarning, this);
4433
0
  if (!cm::contains(this->UsedCliVariables, var)) {
4434
0
    this->UsedCliVariables[var] = false;
4435
0
  }
4436
0
#endif
4437
0
}
4438
4439
void cmake::UnwatchUnusedCli(std::string const& var)
4440
3
{
4441
3
#ifndef CMAKE_BOOTSTRAP
4442
3
  this->VariableWatch->RemoveWatch(var, cmWarnUnusedCliWarning);
4443
3
  this->UsedCliVariables.erase(var);
4444
3
#endif
4445
3
}
4446
4447
void cmake::RunCheckForUnusedVariables()
4448
0
{
4449
0
#ifndef CMAKE_BOOTSTRAP
4450
0
  cmDiagnosticAction const action =
4451
0
    this->CurrentSnapshot.GetDiagnostic(cmDiagnostics::CMD_UNUSED_CLI);
4452
0
  if (action != cmDiagnostics::Ignore) {
4453
0
    bool haveUnused = false;
4454
0
    std::ostringstream msg;
4455
0
    msg << "Manually-specified variables were not used by the project:";
4456
0
    for (auto const& it : this->UsedCliVariables) {
4457
0
      if (!it.second) {
4458
0
        haveUnused = true;
4459
0
        msg << "\n  " << it.first;
4460
0
      }
4461
0
    }
4462
0
    if (haveUnused) {
4463
0
      this->IssueDiagnostic(cmDiagnostics::CMD_UNUSED_CLI, msg.str());
4464
0
    }
4465
0
  }
4466
0
#endif
4467
0
}
4468
4469
void cmake::SetDebugFindOutputPkgs(std::string const& args)
4470
0
{
4471
0
  this->DebugFindPkgs.emplace(args);
4472
0
}
4473
4474
void cmake::SetDebugFindOutputVars(std::string const& args)
4475
0
{
4476
0
  this->DebugFindVars.emplace(args);
4477
0
}
4478
4479
bool cmake::GetDebugFindOutput(std::string const& var) const
4480
0
{
4481
0
  return this->DebugFindVars.count(var);
4482
0
}
4483
4484
bool cmake::GetDebugFindPkgOutput(std::string const& pkg) const
4485
0
{
4486
0
  return this->DebugFindPkgs.count(pkg);
4487
0
}
4488
4489
void cmake::SetCMakeListName(std::string const& name)
4490
0
{
4491
0
  this->CMakeListName = name;
4492
0
}
4493
4494
std::string cmake::GetCMakeListFile(std::string const& dir) const
4495
0
{
4496
0
  assert(!dir.empty());
4497
0
  cm::string_view const slash = dir.back() != '/' ? "/"_s : ""_s;
4498
0
  std::string listFile;
4499
0
  if (!this->CMakeListName.empty()) {
4500
0
    listFile = cmStrCat(dir, slash, this->CMakeListName);
4501
0
  }
4502
0
  if (listFile.empty() || !cmSystemTools::FileExists(listFile, true)) {
4503
0
    listFile = cmStrCat(dir, slash, "CMakeLists.txt");
4504
0
  }
4505
0
  return listFile;
4506
0
}
4507
4508
#if !defined(CMAKE_BOOTSTRAP)
4509
cmMakefileProfilingData& cmake::GetProfilingOutput()
4510
0
{
4511
0
  return *(this->ProfilingOutput);
4512
0
}
4513
4514
bool cmake::IsProfilingEnabled() const
4515
0
{
4516
0
  return static_cast<bool>(this->ProfilingOutput);
4517
0
}
4518
#endif