Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmLocalGenerator.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 "cmLocalGenerator.h"
4
5
#include <algorithm>
6
#include <array>
7
#include <cassert>
8
#include <cctype>
9
#include <cstdio>
10
#include <cstdlib>
11
#include <initializer_list>
12
#include <iterator>
13
#include <queue>
14
#include <sstream>
15
#include <unordered_set>
16
#include <utility>
17
#include <vector>
18
19
#include <cm/memory>
20
#include <cm/optional>
21
#include <cm/string_view>
22
#include <cmext/algorithm>
23
#include <cmext/string_view>
24
25
#include "cmsys/RegularExpression.hxx"
26
27
#include "cmAlgorithms.h"
28
#include "cmCMakePath.h"
29
#include "cmComputeLinkInformation.h"
30
#include "cmCryptoHash.h"
31
#include "cmCustomCommand.h"
32
#include "cmCustomCommandGenerator.h"
33
#include "cmCustomCommandLines.h"
34
#include "cmCustomCommandTypes.h"
35
#include "cmGeneratedFileStream.h"
36
#include "cmGeneratorExpression.h"
37
#include "cmGeneratorExpressionEvaluationFile.h"
38
#include "cmGeneratorTarget.h"
39
#include "cmGlobalGenerator.h"
40
#include "cmInstallGenerator.h"
41
#include "cmInstallScriptGenerator.h"
42
#include "cmInstallTargetGenerator.h"
43
#include "cmLinkLineComputer.h"
44
#include "cmLinkLineDeviceComputer.h"
45
#include "cmList.h"
46
#include "cmMakefile.h"
47
#include "cmMessageType.h"
48
#include "cmObjectLocation.h"
49
#include "cmRange.h"
50
#include "cmRulePlaceholderExpander.h"
51
#include "cmSourceFile.h"
52
#include "cmSourceFileLocation.h"
53
#include "cmSourceFileLocationKind.h"
54
#include "cmSourceGroup.h"
55
#include "cmStandardLevelResolver.h"
56
#include "cmState.h"
57
#include "cmStateDirectory.h"
58
#include "cmStateTypes.h"
59
#include "cmStringAlgorithms.h"
60
#include "cmSystemTools.h"
61
#include "cmTarget.h"
62
#include "cmTestGenerator.h"
63
#include "cmValue.h"
64
#include "cmake.h"
65
66
#if defined(__HAIKU__)
67
#  include <FindDirectory.h>
68
#  include <StorageDefs.h>
69
#endif
70
71
namespace {
72
// List of variables that are replaced when
73
// rules are expanded.  These variables are
74
// replaced in the form <var> with GetSafeDefinition(var).
75
// ${LANG} is replaced in the variable first with all enabled
76
// languages.
77
auto ruleReplaceVars = {
78
  "CMAKE_${LANG}_COMPILER",
79
  "CMAKE_SHARED_MODULE_${LANG}_FLAGS",
80
  "CMAKE_SHARED_LIBRARY_${LANG}_FLAGS",
81
  "CMAKE_SHARED_LIBRARY_SONAME_${LANG}_FLAG",
82
  "CMAKE_${LANG}_ARCHIVE",
83
  "CMAKE_AR",
84
  "CMAKE_SOURCE_DIR",
85
  "CMAKE_BINARY_DIR",
86
  "CMAKE_CURRENT_SOURCE_DIR",
87
  "CMAKE_CURRENT_BINARY_DIR",
88
  "CMAKE_RANLIB",
89
  "CMAKE_MT",
90
  "CMAKE_TAPI",
91
  "CMAKE_CUDA_HOST_COMPILER",
92
  "CMAKE_CUDA_HOST_LINK_LAUNCHER",
93
  "CMAKE_HIP_HOST_COMPILER",
94
  "CMAKE_HIP_HOST_LINK_LAUNCHER",
95
  "CMAKE_CL_SHOWINCLUDES_PREFIX",
96
};
97
98
// Variables whose placeholders now map to an empty string.
99
// Our platform modules no longer use these, but third-party code might.
100
auto ruleReplaceEmptyVars = {
101
  "CMAKE_SHARED_LIBRARY_CREATE_${LANG}_FLAGS",
102
  "CMAKE_SHARED_MODULE_CREATE_${LANG}_FLAGS",
103
  "CMAKE_${LANG}_LINK_FLAGS",
104
};
105
}
106
107
cmLocalGenerator::cmLocalGenerator(cmGlobalGenerator* gg, cmMakefile* makefile)
108
0
  : cmOutputConverter(makefile->GetStateSnapshot())
109
0
  , DirectoryBacktrace(makefile->GetBacktrace())
110
0
{
111
0
  this->GlobalGenerator = gg;
112
113
0
  this->Makefile = makefile;
114
115
0
  this->AliasTargets = makefile->GetAliasTargets();
116
117
0
  this->EmitUniversalBinaryFlags = true;
118
119
0
  this->ComputeObjectMaxPath();
120
121
  // Canonicalize entries of the CPATH environment variable the same
122
  // way detection of CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES does.
123
0
  {
124
0
    std::vector<std::string> cpath;
125
0
    cmSystemTools::GetPath(cpath, "CPATH");
126
0
    for (std::string const& cp : cpath) {
127
0
      if (cmSystemTools::FileIsFullPath(cp)) {
128
0
        this->EnvCPATH.emplace_back(cmSystemTools::CollapseFullPath(cp));
129
0
      }
130
0
    }
131
0
  }
132
133
0
  std::vector<std::string> enabledLanguages =
134
0
    this->GetState()->GetEnabledLanguages();
135
136
0
  if (cmValue sysrootCompile =
137
0
        this->Makefile->GetDefinition("CMAKE_SYSROOT_COMPILE")) {
138
0
    this->CompilerSysroot = *sysrootCompile;
139
0
  } else {
140
0
    this->CompilerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT");
141
0
  }
142
143
0
  if (cmValue sysrootLink =
144
0
        this->Makefile->GetDefinition("CMAKE_SYSROOT_LINK")) {
145
0
    this->LinkerSysroot = *sysrootLink;
146
0
  } else {
147
0
    this->LinkerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT");
148
0
  }
149
150
  // OSX SYSROOT can be required by some tools, like tapi
151
0
  {
152
0
    cmValue osxSysroot = this->Makefile->GetDefinition("CMAKE_OSX_SYSROOT");
153
0
    this->VariableMappings["CMAKE_OSX_SYSROOT"] =
154
0
      osxSysroot.IsEmpty() ? "/" : this->EscapeForShell(*osxSysroot, true);
155
0
  }
156
157
0
  if (cmValue appleArchSysroots =
158
0
        this->Makefile->GetDefinition("CMAKE_APPLE_ARCH_SYSROOTS")) {
159
0
    std::string const& appleArchs =
160
0
      this->Makefile->GetSafeDefinition("CMAKE_OSX_ARCHITECTURES");
161
0
    cmList archs(appleArchs);
162
0
    cmList sysroots{ appleArchSysroots, cmList::EmptyElements::Yes };
163
0
    if (archs.size() == sysroots.size()) {
164
0
      for (cmList::size_type i = 0; i < archs.size(); ++i) {
165
0
        this->AppleArchSysroots[archs[i]] = sysroots[i];
166
0
      }
167
0
    } else {
168
0
      std::string const e =
169
0
        cmStrCat("CMAKE_APPLE_ARCH_SYSROOTS:\n  ", *appleArchSysroots,
170
0
                 "\n"
171
0
                 "is not the same length as CMAKE_OSX_ARCHITECTURES:\n  ",
172
0
                 appleArchs);
173
0
      this->IssueMessage(MessageType::FATAL_ERROR, e);
174
0
    }
175
0
  }
176
177
0
  for (std::string const& lang : enabledLanguages) {
178
0
    if (lang == "NONE") {
179
0
      continue;
180
0
    }
181
0
    this->Compilers["CMAKE_" + lang + "_COMPILER"] = lang;
182
183
0
    this->VariableMappings["CMAKE_" + lang + "_COMPILER"] =
184
0
      this->Makefile->GetSafeDefinition("CMAKE_" + lang + "_COMPILER");
185
186
0
    std::string const& compilerArg1 = "CMAKE_" + lang + "_COMPILER_ARG1";
187
0
    std::string const& compilerTarget = "CMAKE_" + lang + "_COMPILER_TARGET";
188
0
    std::string const& compilerOptionTarget =
189
0
      "CMAKE_" + lang + "_COMPILE_OPTIONS_TARGET";
190
0
    std::string const& compilerExternalToolchain =
191
0
      "CMAKE_" + lang + "_COMPILER_EXTERNAL_TOOLCHAIN";
192
0
    std::string const& compilerOptionExternalToolchain =
193
0
      "CMAKE_" + lang + "_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN";
194
0
    std::string const& compilerOptionSysroot =
195
0
      "CMAKE_" + lang + "_COMPILE_OPTIONS_SYSROOT";
196
197
0
    this->VariableMappings[compilerArg1] =
198
0
      this->Makefile->GetSafeDefinition(compilerArg1);
199
0
    this->VariableMappings[compilerTarget] =
200
0
      this->Makefile->GetSafeDefinition(compilerTarget);
201
0
    this->VariableMappings[compilerOptionTarget] =
202
0
      this->Makefile->GetSafeDefinition(compilerOptionTarget);
203
0
    this->VariableMappings[compilerExternalToolchain] =
204
0
      this->Makefile->GetSafeDefinition(compilerExternalToolchain);
205
0
    this->VariableMappings[compilerOptionExternalToolchain] =
206
0
      this->Makefile->GetSafeDefinition(compilerOptionExternalToolchain);
207
0
    this->VariableMappings[compilerOptionSysroot] =
208
0
      this->Makefile->GetSafeDefinition(compilerOptionSysroot);
209
210
0
    for (std::string replaceVar : ruleReplaceVars) {
211
0
      if (replaceVar.find("${LANG}") != std::string::npos) {
212
0
        cmSystemTools::ReplaceString(replaceVar, "${LANG}", lang);
213
0
      }
214
215
0
      this->VariableMappings[replaceVar] =
216
0
        this->Makefile->GetSafeDefinition(replaceVar);
217
0
    }
218
219
0
    for (std::string replaceVar : ruleReplaceEmptyVars) {
220
0
      if (replaceVar.find("${LANG}") != std::string::npos) {
221
0
        cmSystemTools::ReplaceString(replaceVar, "${LANG}", lang);
222
0
      }
223
224
0
      this->VariableMappings[replaceVar] = std::string();
225
0
    }
226
0
  }
227
0
}
228
229
std::unique_ptr<cmRulePlaceholderExpander>
230
cmLocalGenerator::CreateRulePlaceholderExpander(cmBuildStep buildStep) const
231
0
{
232
0
  return cm::make_unique<cmRulePlaceholderExpander>(
233
0
    buildStep, this->Compilers, this->VariableMappings, this->CompilerSysroot,
234
0
    this->LinkerSysroot);
235
0
}
236
237
0
cmLocalGenerator::~cmLocalGenerator() = default;
238
239
void cmLocalGenerator::IssueMessage(MessageType t,
240
                                    std::string const& text) const
241
0
{
242
0
  this->GetCMakeInstance()->IssueMessage(t, text, this->DirectoryBacktrace);
243
0
}
244
245
void cmLocalGenerator::ComputeObjectMaxPath()
246
0
{
247
// Choose a maximum object file name length.
248
#if defined(_WIN32) || defined(__CYGWIN__)
249
  this->ObjectPathMax = 250;
250
#else
251
0
  this->ObjectPathMax = 1000;
252
0
#endif
253
0
  cmValue plen = this->Makefile->GetDefinition("CMAKE_OBJECT_PATH_MAX");
254
0
  if (cmNonempty(plen)) {
255
0
    unsigned int pmax;
256
0
    if (sscanf(plen->c_str(), "%u", &pmax) == 1) {
257
0
      if (pmax >= 128) {
258
0
        this->ObjectPathMax = pmax;
259
0
      } else {
260
0
        std::ostringstream w;
261
0
        w << "CMAKE_OBJECT_PATH_MAX is set to " << pmax
262
0
          << ", which is less than the minimum of 128.  "
263
0
             "The value will be ignored.";
264
0
        this->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
265
0
      }
266
0
    } else {
267
0
      std::ostringstream w;
268
0
      w << "CMAKE_OBJECT_PATH_MAX is set to \"" << *plen
269
0
        << "\", which fails to parse as a positive integer.  "
270
0
           "The value will be ignored.";
271
0
      this->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
272
0
    }
273
0
  }
274
0
  this->ObjectMaxPathViolations.clear();
275
0
}
276
277
static void MoveSystemIncludesToEnd(std::vector<std::string>& includeDirs,
278
                                    std::string const& config,
279
                                    std::string const& lang,
280
                                    cmGeneratorTarget const* target)
281
0
{
282
0
  if (!target) {
283
0
    return;
284
0
  }
285
286
0
  std::stable_sort(
287
0
    includeDirs.begin(), includeDirs.end(),
288
0
    [&target, &config, &lang](std::string const& a, std::string const& b) {
289
0
      return !target->IsSystemIncludeDirectory(a, config, lang) &&
290
0
        target->IsSystemIncludeDirectory(b, config, lang);
291
0
    });
292
0
}
293
294
static void MoveSystemIncludesToEnd(std::vector<BT<std::string>>& includeDirs,
295
                                    std::string const& config,
296
                                    std::string const& lang,
297
                                    cmGeneratorTarget const* target)
298
0
{
299
0
  if (!target) {
300
0
    return;
301
0
  }
302
303
0
  std::stable_sort(includeDirs.begin(), includeDirs.end(),
304
0
                   [target, &config, &lang](BT<std::string> const& a,
305
0
                                            BT<std::string> const& b) {
306
0
                     return !target->IsSystemIncludeDirectory(a.Value, config,
307
0
                                                              lang) &&
308
0
                       target->IsSystemIncludeDirectory(b.Value, config, lang);
309
0
                   });
310
0
}
311
312
void cmLocalGenerator::TraceDependencies() const
313
0
{
314
  // Generate the rule files for each target.
315
0
  auto const& targets = this->GetGeneratorTargets();
316
0
  for (auto const& target : targets) {
317
0
    if (!target->IsInBuildSystem()) {
318
0
      continue;
319
0
    }
320
0
    target->TraceDependencies();
321
0
  }
322
0
}
323
324
#ifndef CMAKE_BOOTSTRAP
325
void cmLocalGenerator::ResolveSourceGroupGenex()
326
0
{
327
0
  this->Makefile->ResolveSourceGroupGenex(this);
328
0
}
329
#endif
330
331
void cmLocalGenerator::GenerateTestFiles()
332
0
{
333
0
  if (!this->Makefile->IsOn("CMAKE_TESTING_ENABLED")) {
334
0
    return;
335
0
  }
336
337
  // Compute the set of configurations.
338
0
  std::vector<std::string> configurationTypes =
339
0
    this->Makefile->GetGeneratorConfigs(cmMakefile::OnlyMultiConfig);
340
0
  std::string config = this->Makefile->GetDefaultConfiguration();
341
342
0
  std::string file =
343
0
    cmStrCat(this->StateSnapshot.GetDirectory().GetCurrentBinary(),
344
0
             "/CTestTestfile.cmake");
345
0
  this->GlobalGenerator->AddTestFile(file);
346
347
0
  cmGeneratedFileStream fout(file);
348
349
0
  fout << "# CMake generated Testfile for \n"
350
0
          "# Source directory: "
351
0
       << this->StateSnapshot.GetDirectory().GetCurrentSource()
352
0
       << "\n"
353
0
          "# Build directory: "
354
0
       << this->StateSnapshot.GetDirectory().GetCurrentBinary()
355
0
       << "\n"
356
0
          "# \n"
357
0
          "# This file includes the relevant testing commands "
358
0
          "required for \n"
359
0
          "# testing this directory and lists subdirectories to "
360
0
          "be tested as well.\n";
361
362
0
  std::string resourceSpecFile =
363
0
    this->Makefile->GetSafeDefinition("CTEST_RESOURCE_SPEC_FILE");
364
0
  if (!resourceSpecFile.empty()) {
365
0
    fout << "set(CTEST_RESOURCE_SPEC_FILE \"" << resourceSpecFile << "\")\n";
366
0
  }
367
368
0
  cmValue testIncludeFile = this->Makefile->GetProperty("TEST_INCLUDE_FILE");
369
0
  if (testIncludeFile) {
370
0
    fout << "include(\"" << *testIncludeFile << "\")\n";
371
0
  }
372
373
0
  cmValue testIncludeFiles = this->Makefile->GetProperty("TEST_INCLUDE_FILES");
374
0
  if (testIncludeFiles) {
375
0
    cmList includesList{ *testIncludeFiles };
376
0
    for (std::string const& i : includesList) {
377
0
      fout << "include(\"" << i << "\")\n";
378
0
    }
379
0
  }
380
381
  // Ask each test generator to write its code.
382
0
  for (auto const& tester : this->Makefile->GetTestGenerators()) {
383
0
    tester->Compute(this);
384
0
    tester->Generate(fout, config, configurationTypes);
385
0
  }
386
0
  using vec_t = std::vector<cmStateSnapshot>;
387
0
  vec_t const& children = this->Makefile->GetStateSnapshot().GetChildren();
388
0
  for (cmStateSnapshot const& i : children) {
389
    // TODO: Use add_subdirectory instead?
390
0
    std::string outP = i.GetDirectory().GetCurrentBinary();
391
0
    outP = this->MaybeRelativeToCurBinDir(outP);
392
0
    outP = cmOutputConverter::EscapeForCMake(outP);
393
0
    fout << "subdirs(" << outP << ")\n";
394
0
  }
395
396
  // Add directory labels property
397
0
  cmValue directoryLabels =
398
0
    this->Makefile->GetDefinition("CMAKE_DIRECTORY_LABELS");
399
0
  cmValue labels = this->Makefile->GetProperty("LABELS");
400
401
0
  if (labels || directoryLabels) {
402
0
    fout << "set_directory_properties(PROPERTIES LABELS ";
403
0
    if (labels) {
404
0
      fout << cmOutputConverter::EscapeForCMake(*labels);
405
0
    }
406
0
    if (labels && directoryLabels) {
407
0
      fout << ";";
408
0
    }
409
0
    if (directoryLabels) {
410
0
      fout << cmOutputConverter::EscapeForCMake(*directoryLabels);
411
0
    }
412
0
    fout << ")\n";
413
0
  }
414
0
}
415
416
void cmLocalGenerator::CreateEvaluationFileOutputs()
417
0
{
418
0
  std::vector<std::string> const& configs =
419
0
    this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
420
0
  for (std::string const& c : configs) {
421
0
    this->CreateEvaluationFileOutputs(c);
422
0
  }
423
0
}
424
425
void cmLocalGenerator::CreateEvaluationFileOutputs(std::string const& config)
426
0
{
427
0
  for (auto const& geef : this->Makefile->GetEvaluationFiles()) {
428
0
    geef->CreateOutputFile(this, config);
429
0
  }
430
0
}
431
432
void cmLocalGenerator::ProcessEvaluationFiles(
433
  std::vector<std::string>& generatedFiles)
434
0
{
435
0
  for (auto const& geef : this->Makefile->GetEvaluationFiles()) {
436
0
    geef->Generate(this);
437
0
    if (cmSystemTools::GetFatalErrorOccurred()) {
438
0
      return;
439
0
    }
440
0
    std::vector<std::string> files = geef->GetFiles();
441
0
    std::sort(files.begin(), files.end());
442
443
0
    std::vector<std::string> intersection;
444
0
    std::set_intersection(files.begin(), files.end(), generatedFiles.begin(),
445
0
                          generatedFiles.end(),
446
0
                          std::back_inserter(intersection));
447
0
    if (!intersection.empty()) {
448
0
      cmSystemTools::Error("Files to be generated by multiple different "
449
0
                           "commands: " +
450
0
                           cmWrap('"', intersection, '"', " "));
451
0
      return;
452
0
    }
453
454
0
    cm::append(generatedFiles, files);
455
0
    std::inplace_merge(generatedFiles.begin(),
456
0
                       generatedFiles.end() - files.size(),
457
0
                       generatedFiles.end());
458
0
  }
459
0
}
460
461
void cmLocalGenerator::GenerateInstallRules()
462
0
{
463
  // Compute the install prefix.
464
0
  cmValue installPrefix =
465
0
    this->Makefile->GetDefinition("CMAKE_INSTALL_PREFIX");
466
0
  std::string prefix = *installPrefix;
467
468
#if defined(_WIN32) && !defined(__CYGWIN__)
469
  if (!installPrefix) {
470
    if (!cmSystemTools::GetEnv("SystemDrive", prefix)) {
471
      prefix = "C:";
472
    }
473
    cmValue project_name = this->Makefile->GetDefinition("PROJECT_NAME");
474
    if (cmNonempty(project_name)) {
475
      prefix += "/Program Files/";
476
      prefix += *project_name;
477
    } else {
478
      prefix += "/InstalledCMakeProject";
479
    }
480
  }
481
#elif defined(__HAIKU__)
482
  char dir[B_PATH_NAME_LENGTH];
483
  if (!installPrefix) {
484
    if (find_directory(B_SYSTEM_DIRECTORY, -1, false, dir, sizeof(dir)) ==
485
        B_OK) {
486
      prefix = dir;
487
    } else {
488
      prefix = "/boot/system";
489
    }
490
  }
491
#else
492
0
  if (!installPrefix) {
493
0
    prefix = "/usr/local";
494
0
  }
495
0
#endif
496
0
  if (cmValue stagingPrefix =
497
0
        this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX")) {
498
0
    prefix = *stagingPrefix;
499
0
  }
500
501
  // Compute the set of configurations.
502
0
  std::vector<std::string> configurationTypes =
503
0
    this->Makefile->GetGeneratorConfigs(cmMakefile::OnlyMultiConfig);
504
0
  std::string config = this->Makefile->GetDefaultConfiguration();
505
506
  // Choose a default install configuration.
507
0
  std::string default_config = config;
508
0
  char const* default_order[] = { "RELEASE", "MINSIZEREL", "RELWITHDEBINFO",
509
0
                                  "DEBUG", nullptr };
510
0
  for (char const** c = default_order; *c && default_config.empty(); ++c) {
511
0
    for (std::string const& configurationType : configurationTypes) {
512
0
      if (cmSystemTools::UpperCase(configurationType) == *c) {
513
0
        default_config = configurationType;
514
0
      }
515
0
    }
516
0
  }
517
0
  if (default_config.empty() && !configurationTypes.empty()) {
518
0
    default_config = configurationTypes[0];
519
0
  }
520
521
  // Create the install script file.
522
0
  std::string file = this->StateSnapshot.GetDirectory().GetCurrentBinary();
523
0
  std::string homedir = this->GetState()->GetBinaryDirectory();
524
0
  int toplevel_install = 0;
525
0
  if (file == homedir) {
526
0
    toplevel_install = 1;
527
0
  }
528
0
  file += "/cmake_install.cmake";
529
0
  this->GetGlobalGenerator()->AddInstallScript(file);
530
0
  cmGeneratedFileStream fout(file);
531
532
  // Write the header.
533
  /* clang-format off */
534
0
  fout << "# Install script for directory: "
535
0
       << this->StateSnapshot.GetDirectory().GetCurrentSource()
536
0
       << "\n\n"
537
0
          "# Set the install prefix\n"
538
0
          "if(NOT DEFINED CMAKE_INSTALL_PREFIX)\n"
539
0
          "  set(CMAKE_INSTALL_PREFIX \"" << prefix << "\")\n"
540
0
          "endif()\n"
541
0
       << R"(string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX )"
542
0
       << "\"${CMAKE_INSTALL_PREFIX}\")\n\n";
543
  /* clang-format on */
544
545
  // Write support code for generating per-configuration install rules.
546
  /* clang-format off */
547
0
  fout <<
548
0
    "# Set the install configuration name.\n"
549
0
    "if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)\n"
550
0
    "  if(BUILD_TYPE)\n"
551
0
    "    string(REGEX REPLACE \"^[^A-Za-z0-9_]+\" \"\"\n"
552
0
    "           CMAKE_INSTALL_CONFIG_NAME \"${BUILD_TYPE}\")\n"
553
0
    "  else()\n"
554
0
    "    set(CMAKE_INSTALL_CONFIG_NAME \"" << default_config << "\")\n"
555
0
    "  endif()\n"
556
0
    "  message(STATUS \"Install configuration: "
557
0
    "\\\"${CMAKE_INSTALL_CONFIG_NAME}\\\"\")\n"
558
0
    "endif()\n"
559
0
    "\n";
560
  /* clang-format on */
561
562
  // Write support code for dealing with component-specific installs.
563
  /* clang-format off */
564
0
  fout <<
565
0
    "# Set the component getting installed.\n"
566
0
    "if(NOT CMAKE_INSTALL_COMPONENT)\n"
567
0
    "  if(COMPONENT)\n"
568
0
    "    message(STATUS \"Install component: \\\"${COMPONENT}\\\"\")\n"
569
0
    "    set(CMAKE_INSTALL_COMPONENT \"${COMPONENT}\")\n"
570
0
    "  else()\n"
571
0
    "    set(CMAKE_INSTALL_COMPONENT)\n"
572
0
    "  endif()\n"
573
0
    "endif()\n"
574
0
    "\n";
575
  /* clang-format on */
576
577
  // Copy user-specified install options to the install code.
578
0
  if (cmValue so_no_exe =
579
0
        this->Makefile->GetDefinition("CMAKE_INSTALL_SO_NO_EXE")) {
580
    /* clang-format off */
581
0
    fout <<
582
0
      "# Install shared libraries without execute permission?\n"
583
0
      "if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)\n"
584
0
      "  set(CMAKE_INSTALL_SO_NO_EXE \"" << *so_no_exe << "\")\n"
585
0
      "endif()\n"
586
0
      "\n";
587
    /* clang-format on */
588
0
  }
589
590
  // Copy cmake cross compile state to install code.
591
0
  if (cmValue crosscompiling =
592
0
        this->Makefile->GetDefinition("CMAKE_CROSSCOMPILING")) {
593
    /* clang-format off */
594
0
    fout <<
595
0
      "# Is this installation the result of a crosscompile?\n"
596
0
      "if(NOT DEFINED CMAKE_CROSSCOMPILING)\n"
597
0
      "  set(CMAKE_CROSSCOMPILING \"" << *crosscompiling << "\")\n"
598
0
      "endif()\n"
599
0
      "\n";
600
    /* clang-format on */
601
0
  }
602
603
  // Write default directory permissions.
604
0
  if (cmValue defaultDirPermissions = this->Makefile->GetDefinition(
605
0
        "CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS")) {
606
    /* clang-format off */
607
0
    fout <<
608
0
      "# Set default install directory permissions.\n"
609
0
      "if(NOT DEFINED CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS)\n"
610
0
      "  set(CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS \""
611
0
         << *defaultDirPermissions << "\")\n"
612
0
      "endif()\n"
613
0
      "\n";
614
    /* clang-format on */
615
0
  }
616
617
  // Write out CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM so that
618
  // installed code that uses `file(GET_RUNTIME_DEPENDENCIES)`
619
  // has same platform variable as when running cmake
620
0
  if (cmValue platform = this->Makefile->GetDefinition(
621
0
        "CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM")) {
622
    /* clang-format off */
623
0
    fout <<
624
0
      "# Set OS and executable format for runtime-dependencies.\n"
625
0
      "if(NOT DEFINED CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM)\n"
626
0
      "  set(CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM \""
627
0
         << *platform << "\")\n"
628
0
      "endif()\n"
629
0
      "\n";
630
    /* clang-format on */
631
0
  }
632
633
  // Write out CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL so that
634
  // installed code that uses `file(GET_RUNTIME_DEPENDENCIES)`
635
  // has same tool selected as when running cmake
636
0
  if (cmValue command =
637
0
        this->Makefile->GetDefinition("CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL")) {
638
    /* clang-format off */
639
0
    fout <<
640
0
      "# Set tool for dependency-resolution of runtime-dependencies.\n"
641
0
      "if(NOT DEFINED CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL)\n"
642
0
      "  set(CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL \""
643
0
         << *command << "\")\n"
644
0
      "endif()\n"
645
0
      "\n";
646
    /* clang-format on */
647
0
  }
648
649
  // Write out CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND so that
650
  // installed code that uses `file(GET_RUNTIME_DEPENDENCIES)`
651
  // has same path to the tool as when running cmake
652
0
  if (cmValue command = this->Makefile->GetDefinition(
653
0
        "CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND")) {
654
    /* clang-format off */
655
0
    fout <<
656
0
      "# Set path to tool for dependency-resolution of runtime-dependencies.\n"
657
0
      "if(NOT DEFINED CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND)\n"
658
0
      "  set(CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND \""
659
0
         << *command << "\")\n"
660
0
      "endif()\n"
661
0
      "\n";
662
    /* clang-format on */
663
0
  }
664
665
  // Write out CMAKE_OBJDUMP so that installed code that uses
666
  // `file(GET_RUNTIME_DEPENDENCIES)` and hasn't specified
667
  // CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND has consistent
668
  // logic to fallback to CMAKE_OBJDUMP when `objdump` is
669
  // not on the path
670
0
  if (cmValue command = this->Makefile->GetDefinition("CMAKE_OBJDUMP")) {
671
    /* clang-format off */
672
0
    fout <<
673
0
      "# Set path to fallback-tool for dependency-resolution.\n"
674
0
      "if(NOT DEFINED CMAKE_OBJDUMP)\n"
675
0
      "  set(CMAKE_OBJDUMP \""
676
0
         << *command << "\")\n"
677
0
      "endif()\n"
678
0
      "\n";
679
    /* clang-format on */
680
0
  }
681
682
0
  this->AddGeneratorSpecificInstallSetup(fout);
683
684
  // Ask each install generator to write its code.
685
0
  cmPolicies::PolicyStatus status = this->GetPolicyStatus(cmPolicies::CMP0082);
686
0
  auto const& installers = this->Makefile->GetInstallGenerators();
687
0
  bool haveSubdirectoryInstall = false;
688
0
  bool haveInstallAfterSubdirectory = false;
689
0
  if (status == cmPolicies::WARN) {
690
0
    for (auto const& installer : installers) {
691
0
      installer->CheckCMP0082(haveSubdirectoryInstall,
692
0
                              haveInstallAfterSubdirectory);
693
0
      installer->Generate(fout, config, configurationTypes);
694
0
    }
695
0
  } else {
696
0
    for (auto const& installer : installers) {
697
0
      installer->Generate(fout, config, configurationTypes);
698
0
    }
699
0
  }
700
701
  // Write rules from old-style specification stored in targets.
702
0
  this->GenerateTargetInstallRules(fout, config, configurationTypes);
703
704
  // Include install scripts from subdirectories.
705
0
  switch (status) {
706
0
    case cmPolicies::WARN:
707
0
      if (haveInstallAfterSubdirectory &&
708
0
          this->Makefile->PolicyOptionalWarningEnabled(
709
0
            "CMAKE_POLICY_WARNING_CMP0082")) {
710
0
        std::ostringstream e;
711
0
        e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0082) << "\n";
712
0
        this->IssueMessage(MessageType::AUTHOR_WARNING, e.str());
713
0
      }
714
0
      CM_FALLTHROUGH;
715
0
    case cmPolicies::OLD: {
716
0
      std::vector<cmStateSnapshot> children =
717
0
        this->Makefile->GetStateSnapshot().GetChildren();
718
0
      if (!children.empty()) {
719
0
        fout << "if(NOT CMAKE_INSTALL_LOCAL_ONLY)\n";
720
0
        fout << "  # Include the install script for each subdirectory.\n";
721
0
        for (cmStateSnapshot const& c : children) {
722
0
          if (!c.GetDirectory().GetPropertyAsBool("EXCLUDE_FROM_ALL")) {
723
0
            std::string odir = c.GetDirectory().GetCurrentBinary();
724
0
            cmSystemTools::ConvertToUnixSlashes(odir);
725
0
            fout << "  include(\"" << odir << "/cmake_install.cmake\")\n";
726
0
          }
727
0
        }
728
0
        fout << "\n";
729
0
        fout << "endif()\n\n";
730
0
      }
731
0
    } break;
732
733
0
    case cmPolicies::NEW:
734
      // NEW behavior is handled in
735
      // cmInstallSubdirectoryGenerator::GenerateScript()
736
0
      break;
737
0
  }
738
739
  /* clang-format off */
740
741
0
    fout <<
742
0
      "string(REPLACE \";\" \"\\n\" CMAKE_INSTALL_MANIFEST_CONTENT\n"
743
0
      "       \"${CMAKE_INSTALL_MANIFEST_FILES}\")\n"
744
0
      "if(CMAKE_INSTALL_LOCAL_ONLY)\n"
745
0
      "  file(WRITE \"" <<
746
0
      this->StateSnapshot.GetDirectory().GetCurrentBinary() <<
747
0
      "/install_local_manifest.txt\"\n"
748
0
      "     \"${CMAKE_INSTALL_MANIFEST_CONTENT}\")\n"
749
0
      "endif()\n";
750
751
0
    if (toplevel_install) {
752
0
      fout <<
753
0
        "if(CMAKE_INSTALL_COMPONENT)\n"
754
0
        "  if(CMAKE_INSTALL_COMPONENT MATCHES \"^[a-zA-Z0-9_.+-]+$\")\n"
755
0
        "    set(CMAKE_INSTALL_MANIFEST \"install_manifest_"
756
0
        "${CMAKE_INSTALL_COMPONENT}.txt\")\n"
757
0
        "  else()\n"
758
0
        "    string(MD5 CMAKE_INST_COMP_HASH \"${CMAKE_INSTALL_COMPONENT}\")\n"
759
0
        "    set(CMAKE_INSTALL_MANIFEST \"install_manifest_"
760
0
        "${CMAKE_INST_COMP_HASH}.txt\")\n"
761
0
        "    unset(CMAKE_INST_COMP_HASH)\n"
762
0
        "  endif()\n"
763
0
        "else()\n"
764
0
        "  set(CMAKE_INSTALL_MANIFEST \"install_manifest.txt\")\n"
765
0
        "endif()\n"
766
0
        "\n"
767
0
        "if(NOT CMAKE_INSTALL_LOCAL_ONLY)\n"
768
0
        "  file(WRITE \"" << homedir << "/${CMAKE_INSTALL_MANIFEST}\"\n"
769
0
        "     \"${CMAKE_INSTALL_MANIFEST_CONTENT}\")\n"
770
0
        "endif()\n";
771
0
    }
772
  /* clang-format on */
773
0
}
774
775
void cmLocalGenerator::AddGeneratorTarget(
776
  std::unique_ptr<cmGeneratorTarget> gt)
777
0
{
778
0
  cmGeneratorTarget* gt_ptr = gt.get();
779
780
0
  this->GeneratorTargets.push_back(std::move(gt));
781
0
  this->GeneratorTargetSearchIndex.emplace(gt_ptr->GetName(), gt_ptr);
782
0
  this->GlobalGenerator->IndexGeneratorTarget(gt_ptr);
783
0
}
784
785
void cmLocalGenerator::AddImportedGeneratorTarget(cmGeneratorTarget* gt)
786
0
{
787
0
  this->ImportedGeneratorTargets.emplace(gt->GetName(), gt);
788
0
  this->GlobalGenerator->IndexGeneratorTarget(gt);
789
0
}
790
791
void cmLocalGenerator::AddOwnedImportedGeneratorTarget(
792
  std::unique_ptr<cmGeneratorTarget> gt)
793
0
{
794
0
  this->OwnedImportedGeneratorTargets.push_back(std::move(gt));
795
0
}
796
797
cmGeneratorTarget* cmLocalGenerator::FindLocalNonAliasGeneratorTarget(
798
  std::string const& name) const
799
0
{
800
0
  auto ti = this->GeneratorTargetSearchIndex.find(name);
801
0
  if (ti != this->GeneratorTargetSearchIndex.end()) {
802
0
    return ti->second;
803
0
  }
804
0
  return nullptr;
805
0
}
806
807
void cmLocalGenerator::ComputeTargetManifest()
808
0
{
809
  // Collect the set of configuration types.
810
0
  std::vector<std::string> configNames =
811
0
    this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
812
813
  // Add our targets to the manifest for each configuration.
814
0
  auto const& targets = this->GetGeneratorTargets();
815
0
  for (auto const& target : targets) {
816
0
    if (!target->IsInBuildSystem()) {
817
0
      continue;
818
0
    }
819
0
    for (std::string const& c : configNames) {
820
0
      target->ComputeTargetManifest(c);
821
0
    }
822
0
  }
823
0
}
824
825
bool cmLocalGenerator::ComputeTargetCompileFeatures()
826
0
{
827
  // Collect the set of configuration types.
828
0
  std::vector<std::string> configNames =
829
0
    this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
830
831
0
  using LanguagePair = std::pair<std::string, std::string>;
832
0
  std::vector<LanguagePair> pairedLanguages{
833
0
    { "OBJC", "C" }, { "OBJCXX", "CXX" }, { "CUDA", "CXX" }, { "HIP", "CXX" }
834
0
  };
835
0
  std::set<LanguagePair> inferredEnabledLanguages;
836
0
  for (auto const& lang : pairedLanguages) {
837
0
    if (this->Makefile->GetState()->GetLanguageEnabled(lang.first)) {
838
0
      inferredEnabledLanguages.insert(lang);
839
0
    }
840
0
  }
841
842
  // Process compile features of all targets.
843
0
  auto const& targets = this->GetGeneratorTargets();
844
0
  for (auto const& target : targets) {
845
0
    for (std::string const& c : configNames) {
846
0
      if (!target->ComputeCompileFeatures(c)) {
847
0
        return false;
848
0
      }
849
0
    }
850
851
    // Now that C/C++ _STANDARD values have been computed
852
    // set the values to ObjC/ObjCXX _STANDARD variables
853
0
    if (target->CanCompileSources()) {
854
0
      for (std::string const& c : configNames) {
855
0
        target->ComputeCompileFeatures(c, inferredEnabledLanguages);
856
0
      }
857
0
    }
858
0
  }
859
860
0
  return true;
861
0
}
862
863
bool cmLocalGenerator::IsRootMakefile() const
864
0
{
865
0
  return !this->StateSnapshot.GetBuildsystemDirectoryParent().IsValid();
866
0
}
867
868
cmState* cmLocalGenerator::GetState() const
869
0
{
870
0
  return this->GlobalGenerator->GetCMakeInstance()->GetState();
871
0
}
872
873
cmStateSnapshot cmLocalGenerator::GetStateSnapshot() const
874
0
{
875
0
  return this->Makefile->GetStateSnapshot();
876
0
}
877
878
std::string cmLocalGenerator::GetRuleLauncher(cmGeneratorTarget* target,
879
                                              std::string const& prop,
880
                                              std::string const& config)
881
0
{
882
0
  cmValue value = this->Makefile->GetProperty(prop);
883
0
  if (target) {
884
0
    value = target->GetProperty(prop);
885
0
  }
886
0
  if (value) {
887
0
    return cmGeneratorExpression::Evaluate(*value, this, config, target);
888
0
  }
889
0
  return "";
890
0
}
891
892
std::string cmLocalGenerator::ConvertToIncludeReference(
893
  std::string const& path, OutputFormat format)
894
0
{
895
0
  return this->ConvertToOutputForExisting(path, format);
896
0
}
897
898
std::string cmLocalGenerator::GetIncludeFlags(
899
  std::vector<std::string> const& includeDirs, cmGeneratorTarget* target,
900
  std::string const& lang, std::string const& config, bool forResponseFile)
901
0
{
902
0
  if (lang.empty()) {
903
0
    return "";
904
0
  }
905
906
0
  std::vector<std::string> includes = includeDirs;
907
0
  MoveSystemIncludesToEnd(includes, config, lang, target);
908
909
0
  OutputFormat shellFormat = forResponseFile ? RESPONSE : SHELL;
910
0
  std::ostringstream includeFlags;
911
912
0
  std::string const& includeFlag =
913
0
    this->Makefile->GetSafeDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_", lang));
914
0
  bool quotePaths = false;
915
0
  if (this->Makefile->GetDefinition("CMAKE_QUOTE_INCLUDE_PATHS")) {
916
0
    quotePaths = true;
917
0
  }
918
0
  std::string sep = " ";
919
0
  bool repeatFlag = true;
920
  // should the include flag be repeated like ie. -IA -IB
921
0
  if (cmValue incSep = this->Makefile->GetDefinition(
922
0
        cmStrCat("CMAKE_INCLUDE_FLAG_SEP_", lang))) {
923
    // if there is a separator then the flag is not repeated but is only
924
    // given once i.e.  -classpath a:b:c
925
0
    sep = *incSep;
926
0
    repeatFlag = false;
927
0
  }
928
929
  // Support special system include flag if it is available and the
930
  // normal flag is repeated for each directory.
931
0
  cmValue sysIncludeFlag = nullptr;
932
0
  cmValue sysIncludeFlagWarning = nullptr;
933
0
  if (repeatFlag) {
934
0
    sysIncludeFlag = this->Makefile->GetDefinition(
935
0
      cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", lang));
936
0
    sysIncludeFlagWarning = this->Makefile->GetDefinition(
937
0
      cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", lang, "_WARNING"));
938
0
  }
939
940
0
  cmValue fwSearchFlag = this->Makefile->GetDefinition(
941
0
    cmStrCat("CMAKE_", lang, "_FRAMEWORK_SEARCH_FLAG"));
942
0
  cmValue sysFwSearchFlag = this->Makefile->GetDefinition(
943
0
    cmStrCat("CMAKE_", lang, "_SYSTEM_FRAMEWORK_SEARCH_FLAG"));
944
945
0
  bool flagUsed = false;
946
0
  bool sysIncludeFlagUsed = false;
947
0
  std::set<std::string> emitted;
948
#ifdef __APPLE__
949
  emitted.insert("/System/Library/Frameworks");
950
#endif
951
0
  for (std::string const& i : includes) {
952
0
    if (cmNonempty(fwSearchFlag) && this->Makefile->IsOn("APPLE") &&
953
0
        cmSystemTools::IsPathToFramework(i)) {
954
0
      std::string const frameworkDir = cmSystemTools::GetFilenamePath(i);
955
0
      if (emitted.insert(frameworkDir).second) {
956
0
        if (sysFwSearchFlag && target &&
957
0
            target->IsSystemIncludeDirectory(frameworkDir, config, lang)) {
958
0
          includeFlags << *sysFwSearchFlag;
959
0
        } else {
960
0
          includeFlags << *fwSearchFlag;
961
0
        }
962
0
        includeFlags << this->ConvertToOutputFormat(frameworkDir, shellFormat)
963
0
                     << " ";
964
0
      }
965
0
      continue;
966
0
    }
967
968
0
    if (!flagUsed || repeatFlag) {
969
0
      if (sysIncludeFlag && target &&
970
0
          target->IsSystemIncludeDirectory(i, config, lang)) {
971
0
        includeFlags << *sysIncludeFlag;
972
0
        sysIncludeFlagUsed = true;
973
0
      } else {
974
0
        includeFlags << includeFlag;
975
0
      }
976
0
      flagUsed = true;
977
0
    }
978
0
    std::string includePath = this->ConvertToIncludeReference(i, shellFormat);
979
0
    if (quotePaths && !includePath.empty() && includePath.front() != '\"') {
980
0
      includeFlags << "\"";
981
0
    }
982
0
    includeFlags << includePath;
983
0
    if (quotePaths && !includePath.empty() && includePath.front() != '\"') {
984
0
      includeFlags << "\"";
985
0
    }
986
0
    includeFlags << sep;
987
0
  }
988
0
  if (sysIncludeFlagUsed && sysIncludeFlagWarning) {
989
0
    includeFlags << *sysIncludeFlagWarning;
990
0
  }
991
0
  std::string flags = includeFlags.str();
992
  // remove trailing separators
993
0
  if ((sep[0] != ' ') && !flags.empty() && flags.back() == sep[0]) {
994
0
    flags.back() = ' ';
995
0
  }
996
0
  return cmTrimWhitespace(flags);
997
0
}
998
999
void cmLocalGenerator::AddCompileOptions(std::string& flags,
1000
                                         cmGeneratorTarget* target,
1001
                                         std::string const& lang,
1002
                                         std::string const& config)
1003
0
{
1004
0
  std::vector<BT<std::string>> tmpFlags;
1005
0
  this->AddCompileOptions(tmpFlags, target, lang, config);
1006
0
  this->AppendFlags(flags, tmpFlags);
1007
0
}
1008
1009
void cmLocalGenerator::AddCompileOptions(std::vector<BT<std::string>>& flags,
1010
                                         cmGeneratorTarget* target,
1011
                                         std::string const& lang,
1012
                                         std::string const& config)
1013
0
{
1014
0
  std::string langFlagRegexVar = cmStrCat("CMAKE_", lang, "_FLAG_REGEX");
1015
1016
0
  if (cmValue langFlagRegexStr =
1017
0
        this->Makefile->GetDefinition(langFlagRegexVar)) {
1018
    // Filter flags acceptable to this language.
1019
0
    if (cmValue targetFlags = target->GetProperty("COMPILE_FLAGS")) {
1020
0
      std::vector<std::string> opts;
1021
0
      cmSystemTools::ParseWindowsCommandLine(targetFlags->c_str(), opts);
1022
      // Re-escape these flags since COMPILE_FLAGS were already parsed
1023
      // as a command line above.
1024
0
      std::string compileOpts;
1025
0
      this->AppendCompileOptions(compileOpts, opts, langFlagRegexStr->c_str());
1026
0
      if (!compileOpts.empty()) {
1027
0
        flags.emplace_back(std::move(compileOpts));
1028
0
      }
1029
0
    }
1030
0
    std::vector<BT<std::string>> targetCompileOpts =
1031
0
      target->GetCompileOptions(config, lang);
1032
    // COMPILE_OPTIONS are escaped.
1033
0
    this->AppendCompileOptions(flags, targetCompileOpts,
1034
0
                               langFlagRegexStr->c_str());
1035
0
  } else {
1036
    // Use all flags.
1037
0
    if (cmValue targetFlags = target->GetProperty("COMPILE_FLAGS")) {
1038
      // COMPILE_FLAGS are not escaped for historical reasons.
1039
0
      std::string compileFlags;
1040
0
      this->AppendFlags(compileFlags, *targetFlags);
1041
0
      if (!compileFlags.empty()) {
1042
0
        flags.emplace_back(std::move(compileFlags));
1043
0
      }
1044
0
    }
1045
0
    std::vector<BT<std::string>> targetCompileOpts =
1046
0
      target->GetCompileOptions(config, lang);
1047
    // COMPILE_OPTIONS are escaped.
1048
0
    this->AppendCompileOptions(flags, targetCompileOpts);
1049
0
  }
1050
1051
0
  cmStandardLevelResolver standardResolver(this->Makefile);
1052
0
  for (auto const& it : target->GetMaxLanguageStandards()) {
1053
0
    cmValue standard = target->GetLanguageStandard(it.first, config);
1054
0
    if (!standard) {
1055
0
      continue;
1056
0
    }
1057
0
    if (standardResolver.IsLaterStandard(it.first, *standard, it.second)) {
1058
0
      std::ostringstream e;
1059
0
      e << "The COMPILE_FEATURES property of target \"" << target->GetName()
1060
0
        << "\" was evaluated when computing the link "
1061
0
           "implementation, and the \""
1062
0
        << it.first << "_STANDARD\" was \"" << it.second
1063
0
        << "\" for that computation.  Computing the "
1064
0
           "COMPILE_FEATURES based on the link implementation resulted in a "
1065
0
           "higher \""
1066
0
        << it.first << "_STANDARD\" \"" << *standard
1067
0
        << "\".  "
1068
0
           "This is not permitted. The COMPILE_FEATURES may not both depend "
1069
0
           "on "
1070
0
           "and be depended on by the link implementation.\n";
1071
0
      this->IssueMessage(MessageType::FATAL_ERROR, e.str());
1072
0
      return;
1073
0
    }
1074
0
  }
1075
1076
  // Add Warning as errors flags
1077
0
  if (!this->GetCMakeInstance()->GetIgnoreCompileWarningAsError()) {
1078
0
    cmValue const wError = target->GetProperty("COMPILE_WARNING_AS_ERROR");
1079
0
    cmValue const wErrorOpts = this->Makefile->GetDefinition(
1080
0
      cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_WARNING_AS_ERROR"));
1081
0
    if (wError.IsOn() && wErrorOpts.IsSet()) {
1082
0
      std::string wErrorFlags;
1083
0
      this->AppendCompileOptions(wErrorFlags, *wErrorOpts);
1084
0
      if (!wErrorFlags.empty()) {
1085
0
        flags.emplace_back(std::move(wErrorFlags));
1086
0
      }
1087
0
    }
1088
0
  }
1089
1090
  // Add compile flag for the MSVC compiler only.
1091
0
  cmMakefile* mf = this->GetMakefile();
1092
0
  if (cmValue jmc =
1093
0
        mf->GetDefinition("CMAKE_" + lang + "_COMPILE_OPTIONS_JMC")) {
1094
1095
    // Handle Just My Code debugging flags, /JMC.
1096
    // If the target is a Managed C++ one, /JMC is not compatible.
1097
0
    if (target->GetManagedType(config) !=
1098
0
        cmGeneratorTarget::ManagedType::Managed) {
1099
      // add /JMC flags if target property VS_JUST_MY_CODE_DEBUGGING is set
1100
      // to ON
1101
0
      if (cmValue jmcExprGen =
1102
0
            target->GetProperty("VS_JUST_MY_CODE_DEBUGGING")) {
1103
0
        std::string isJMCEnabled =
1104
0
          cmGeneratorExpression::Evaluate(*jmcExprGen, this, config);
1105
0
        if (cmIsOn(isJMCEnabled)) {
1106
0
          cmList optList{ *jmc };
1107
0
          std::string jmcFlags;
1108
0
          this->AppendCompileOptions(jmcFlags, optList);
1109
0
          if (!jmcFlags.empty()) {
1110
0
            flags.emplace_back(std::move(jmcFlags));
1111
0
          }
1112
0
        }
1113
0
      }
1114
0
    }
1115
0
  }
1116
0
}
1117
1118
cmTarget* cmLocalGenerator::AddCustomCommandToTarget(
1119
  std::string const& target, cmCustomCommandType type,
1120
  std::unique_ptr<cmCustomCommand> cc, cmObjectLibraryCommands objLibCommands)
1121
0
{
1122
0
  cmTarget* t = this->Makefile->GetCustomCommandTarget(
1123
0
    target, objLibCommands, this->DirectoryBacktrace);
1124
0
  if (!t) {
1125
0
    return nullptr;
1126
0
  }
1127
1128
0
  cc->SetBacktrace(this->DirectoryBacktrace);
1129
1130
0
  detail::AddCustomCommandToTarget(*this, cmCommandOrigin::Generator, t, type,
1131
0
                                   std::move(cc));
1132
1133
0
  return t;
1134
0
}
1135
1136
cmSourceFile* cmLocalGenerator::AddCustomCommandToOutput(
1137
  std::unique_ptr<cmCustomCommand> cc, bool replace)
1138
0
{
1139
  // Make sure there is at least one output.
1140
0
  if (cc->GetOutputs().empty()) {
1141
0
    cmSystemTools::Error("Attempt to add a custom rule with no output!");
1142
0
    return nullptr;
1143
0
  }
1144
1145
0
  cc->SetBacktrace(this->DirectoryBacktrace);
1146
0
  return detail::AddCustomCommandToOutput(*this, cmCommandOrigin::Generator,
1147
0
                                          std::move(cc), replace);
1148
0
}
1149
1150
cmTarget* cmLocalGenerator::AddUtilityCommand(
1151
  std::string const& utilityName, bool excludeFromAll,
1152
  std::unique_ptr<cmCustomCommand> cc)
1153
0
{
1154
0
  cmTarget* target =
1155
0
    this->Makefile->AddNewUtilityTarget(utilityName, excludeFromAll);
1156
0
  target->SetIsGeneratorProvided(true);
1157
1158
0
  if (cc->GetCommandLines().empty() && cc->GetDepends().empty()) {
1159
0
    return target;
1160
0
  }
1161
1162
0
  cc->SetBacktrace(this->DirectoryBacktrace);
1163
0
  detail::AddUtilityCommand(*this, cmCommandOrigin::Generator, target,
1164
0
                            std::move(cc));
1165
1166
0
  return target;
1167
0
}
1168
1169
std::vector<BT<std::string>> cmLocalGenerator::GetIncludeDirectoriesImplicit(
1170
  cmGeneratorTarget const* target, std::string const& lang,
1171
  std::string const& config, bool stripImplicitDirs,
1172
  bool appendAllImplicitDirs) const
1173
0
{
1174
0
  std::vector<BT<std::string>> result;
1175
  // Do not repeat an include path.
1176
0
  std::set<std::string> emitted;
1177
1178
0
  auto emitDir = [&result, &emitted](std::string const& dir) {
1179
0
    if (emitted.insert(dir).second) {
1180
0
      result.emplace_back(dir);
1181
0
    }
1182
0
  };
1183
1184
0
  auto emitBT = [&result, &emitted](BT<std::string> const& dir) {
1185
0
    if (emitted.insert(dir.Value).second) {
1186
0
      result.emplace_back(dir);
1187
0
    }
1188
0
  };
1189
1190
  // When automatic include directories are requested for a build then
1191
  // include the source and binary directories at the beginning of the
1192
  // include path to approximate include file behavior for an
1193
  // in-source build.  This does not account for the case of a source
1194
  // file in a subdirectory of the current source directory but we
1195
  // cannot fix this because not all native build tools support
1196
  // per-source-file include paths.
1197
0
  if (this->Makefile->IsOn("CMAKE_INCLUDE_CURRENT_DIR")) {
1198
    // Current binary directory
1199
0
    emitDir(this->StateSnapshot.GetDirectory().GetCurrentBinary());
1200
    // Current source directory
1201
0
    emitDir(this->StateSnapshot.GetDirectory().GetCurrentSource());
1202
0
  }
1203
1204
0
  if (!target) {
1205
0
    return result;
1206
0
  }
1207
1208
  // Standard include directories to be added unconditionally at the end.
1209
  // These are intended to simulate additional implicit include directories.
1210
0
  cmList userStandardDirs;
1211
0
  {
1212
0
    std::string const value = this->Makefile->GetSafeDefinition(
1213
0
      cmStrCat("CMAKE_", lang, "_STANDARD_INCLUDE_DIRECTORIES"));
1214
0
    userStandardDirs.assign(value);
1215
0
    for (std::string& usd : userStandardDirs) {
1216
0
      cmSystemTools::ConvertToUnixSlashes(usd);
1217
0
    }
1218
0
  }
1219
1220
  // Implicit include directories
1221
0
  std::vector<std::string> implicitDirs;
1222
0
  std::set<std::string> implicitSet;
1223
  // Include directories to be excluded as if they were implicit.
1224
0
  std::set<std::string> implicitExclude;
1225
0
  {
1226
    // Raw list of implicit include directories
1227
    // Start with "standard" directories that we unconditionally add below.
1228
0
    std::vector<std::string> impDirVec = userStandardDirs;
1229
1230
    // Load implicit include directories for this language.
1231
    // We ignore this for Fortran because:
1232
    // * There are no standard library headers to avoid overriding.
1233
    // * Compilers like gfortran do not search their own implicit include
1234
    //   directories for modules ('.mod' files).
1235
0
    if (lang != "Fortran") {
1236
0
      size_t const impDirVecOldSize = impDirVec.size();
1237
0
      cmList::append(impDirVec,
1238
0
                     this->Makefile->GetDefinition(cmStrCat(
1239
0
                       "CMAKE_", lang, "_IMPLICIT_INCLUDE_DIRECTORIES")));
1240
      // FIXME: Use cmRange with 'advance()' when it supports non-const.
1241
0
      for (size_t i = impDirVecOldSize; i < impDirVec.size(); ++i) {
1242
0
        cmSystemTools::ConvertToUnixSlashes(impDirVec[i]);
1243
0
      }
1244
1245
      // The CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES are computed using
1246
      // try_compile in CMAKE_DETERMINE_COMPILER_ABI, but the implicit include
1247
      // directories are not known during that try_compile.  This can be a
1248
      // problem when the HIP runtime include path is /usr/include because the
1249
      // runtime include path is always added to the userDirs and the compiler
1250
      // includes standard library headers via "__clang_hip_runtime_wrapper.h".
1251
0
      if (lang == "HIP" && impDirVec.size() == impDirVecOldSize &&
1252
0
          !cm::contains(impDirVec, "/usr/include")) {
1253
0
        implicitExclude.emplace("/usr/include");
1254
0
      }
1255
0
    }
1256
1257
    // The Platform/UnixPaths module used to hard-code /usr/include for C, CXX,
1258
    // and CUDA in CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES, but those
1259
    // variables are now computed.  On macOS the /usr/include directory is
1260
    // inside the platform SDK so the computed value does not contain it
1261
    // directly.  In this case adding -I/usr/include can hide SDK headers so we
1262
    // must still exclude it.
1263
0
    if ((lang == "C" || lang == "CXX" || lang == "CUDA") &&
1264
0
        !cm::contains(impDirVec, "/usr/include") &&
1265
0
        std::find_if(impDirVec.begin(), impDirVec.end(),
1266
0
                     [](std::string const& d) {
1267
0
                       return cmHasLiteralSuffix(d, "/usr/include");
1268
0
                     }) != impDirVec.end()) {
1269
      // Only exclude this hard coded path for backwards compatibility.
1270
0
      implicitExclude.emplace("/usr/include");
1271
0
    }
1272
1273
0
    for (std::string const& i : impDirVec) {
1274
0
      if (implicitSet.insert(this->GlobalGenerator->GetRealPath(i)).second) {
1275
0
        implicitDirs.emplace_back(i);
1276
0
      }
1277
0
    }
1278
0
  }
1279
1280
0
  bool const isCorCxx = (lang == "C" || lang == "CXX");
1281
1282
  // Resolve symlinks in CPATH for comparison with resolved include paths.
1283
  // We do this here instead of when EnvCPATH is populated in case symlinks
1284
  // on disk have changed in the meantime.
1285
0
  std::set<std::string> resolvedEnvCPATH;
1286
0
  if (isCorCxx) {
1287
0
    for (std::string const& i : this->EnvCPATH) {
1288
0
      resolvedEnvCPATH.emplace(this->GlobalGenerator->GetRealPath(i));
1289
0
    }
1290
0
  }
1291
1292
  // Checks if this is not an excluded (implicit) include directory.
1293
0
  auto notExcluded = [this, &implicitSet, &implicitExclude, &resolvedEnvCPATH,
1294
0
                      isCorCxx](std::string const& dir) -> bool {
1295
0
    std::string const& real_dir = this->GlobalGenerator->GetRealPath(dir);
1296
0
    return
1297
      // Do not exclude directories that are not in any excluded set.
1298
0
      !(cm::contains(implicitSet, real_dir) ||
1299
0
        cm::contains(implicitExclude, dir))
1300
      // Do not exclude entries of the CPATH environment variable even though
1301
      // they are implicitly searched by the compiler.  They are meant to be
1302
      // user-specified directories that can be re-ordered or converted to
1303
      // -isystem without breaking real compiler builtin headers.
1304
0
      || (isCorCxx && cm::contains(resolvedEnvCPATH, real_dir));
1305
0
  };
1306
1307
  // Get the target-specific include directories.
1308
0
  std::vector<BT<std::string>> userDirs =
1309
0
    target->GetIncludeDirectories(config, lang);
1310
1311
  // Support putting all the in-project include directories first if
1312
  // it is requested by the project.
1313
0
  if (this->Makefile->IsOn("CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE")) {
1314
0
    std::string const& topSourceDir = this->GetState()->GetSourceDirectory();
1315
0
    std::string const& topBinaryDir = this->GetState()->GetBinaryDirectory();
1316
0
    for (BT<std::string> const& udr : userDirs) {
1317
      // Emit this directory only if it is a subdirectory of the
1318
      // top-level source or binary tree.
1319
0
      if (cmSystemTools::ComparePath(udr.Value, topSourceDir) ||
1320
0
          cmSystemTools::ComparePath(udr.Value, topBinaryDir) ||
1321
0
          cmSystemTools::IsSubDirectory(udr.Value, topSourceDir) ||
1322
0
          cmSystemTools::IsSubDirectory(udr.Value, topBinaryDir)) {
1323
0
        if (notExcluded(udr.Value)) {
1324
0
          emitBT(udr);
1325
0
        }
1326
0
      }
1327
0
    }
1328
0
  }
1329
1330
  // Emit remaining non implicit user directories.
1331
0
  for (BT<std::string> const& udr : userDirs) {
1332
0
    if (notExcluded(udr.Value)) {
1333
0
      emitBT(udr);
1334
0
    }
1335
0
  }
1336
1337
  // Sort result
1338
0
  MoveSystemIncludesToEnd(result, config, lang, target);
1339
1340
  // Append standard include directories for this language.
1341
0
  userDirs.reserve(userDirs.size() + userStandardDirs.size());
1342
0
  for (std::string& usd : userStandardDirs) {
1343
0
    emitDir(usd);
1344
0
    userDirs.emplace_back(std::move(usd));
1345
0
  }
1346
1347
  // Append compiler implicit include directories
1348
0
  if (!stripImplicitDirs) {
1349
    // Append implicit directories that were requested by the user only
1350
0
    for (BT<std::string> const& udr : userDirs) {
1351
0
      if (cm::contains(implicitSet, cmSystemTools::GetRealPath(udr.Value))) {
1352
0
        emitBT(udr);
1353
0
      }
1354
0
    }
1355
    // Append remaining implicit directories (on demand)
1356
0
    if (appendAllImplicitDirs) {
1357
0
      for (std::string& imd : implicitDirs) {
1358
0
        emitDir(imd);
1359
0
      }
1360
0
    }
1361
0
  }
1362
1363
0
  return result;
1364
0
}
1365
1366
void cmLocalGenerator::GetIncludeDirectoriesImplicit(
1367
  std::vector<std::string>& dirs, cmGeneratorTarget const* target,
1368
  std::string const& lang, std::string const& config, bool stripImplicitDirs,
1369
  bool appendAllImplicitDirs) const
1370
0
{
1371
0
  std::vector<BT<std::string>> tmp = this->GetIncludeDirectoriesImplicit(
1372
0
    target, lang, config, stripImplicitDirs, appendAllImplicitDirs);
1373
0
  dirs.reserve(dirs.size() + tmp.size());
1374
0
  for (BT<std::string>& v : tmp) {
1375
0
    dirs.emplace_back(std::move(v.Value));
1376
0
  }
1377
0
}
1378
1379
std::vector<BT<std::string>> cmLocalGenerator::GetIncludeDirectories(
1380
  cmGeneratorTarget const* target, std::string const& lang,
1381
  std::string const& config) const
1382
0
{
1383
0
  return this->GetIncludeDirectoriesImplicit(target, lang, config);
1384
0
}
1385
1386
void cmLocalGenerator::GetIncludeDirectories(std::vector<std::string>& dirs,
1387
                                             cmGeneratorTarget const* target,
1388
                                             std::string const& lang,
1389
                                             std::string const& config) const
1390
0
{
1391
0
  this->GetIncludeDirectoriesImplicit(dirs, target, lang, config);
1392
0
}
1393
1394
void cmLocalGenerator::GetStaticLibraryFlags(std::string& flags,
1395
                                             std::string const& config,
1396
                                             std::string const& linkLanguage,
1397
                                             cmGeneratorTarget* target)
1398
0
{
1399
0
  std::vector<BT<std::string>> tmpFlags =
1400
0
    this->GetStaticLibraryFlags(config, linkLanguage, target);
1401
0
  this->AppendFlags(flags, tmpFlags);
1402
0
}
1403
1404
std::vector<BT<std::string>> cmLocalGenerator::GetStaticLibraryFlags(
1405
  std::string const& config, std::string const& linkLanguage,
1406
  cmGeneratorTarget* target)
1407
0
{
1408
0
  std::vector<BT<std::string>> flags;
1409
0
  if (linkLanguage != "Swift" && !this->IsSplitSwiftBuild()) {
1410
0
    std::string staticLibFlags;
1411
0
    this->AddConfigVariableFlags(staticLibFlags, "CMAKE_STATIC_LINKER_FLAGS",
1412
0
                                 config);
1413
0
    if (!staticLibFlags.empty()) {
1414
0
      flags.emplace_back(std::move(staticLibFlags));
1415
0
    }
1416
0
  }
1417
1418
0
  std::string staticLibFlags;
1419
0
  std::string const configUpper = cmSystemTools::UpperCase(config);
1420
0
  this->AppendFlags(staticLibFlags,
1421
0
                    target->GetSafeProperty("STATIC_LIBRARY_FLAGS"));
1422
0
  if (!configUpper.empty()) {
1423
0
    std::string name = "STATIC_LIBRARY_FLAGS_" + configUpper;
1424
0
    this->AppendFlags(staticLibFlags, target->GetSafeProperty(name));
1425
0
  }
1426
1427
0
  if (!staticLibFlags.empty()) {
1428
0
    flags.emplace_back(std::move(staticLibFlags));
1429
0
  }
1430
1431
0
  std::vector<BT<std::string>> staticLibOpts =
1432
0
    target->GetStaticLibraryLinkOptions(config, linkLanguage);
1433
  // STATIC_LIBRARY_OPTIONS are escaped.
1434
0
  this->AppendCompileOptions(flags, staticLibOpts);
1435
1436
0
  return flags;
1437
0
}
1438
1439
void cmLocalGenerator::GetDeviceLinkFlags(
1440
  cmLinkLineDeviceComputer& linkLineComputer, std::string const& config,
1441
  std::string& linkLibs, std::string& linkFlags, std::string& frameworkPath,
1442
  std::string& linkPath, cmGeneratorTarget* target)
1443
0
{
1444
0
  cmGeneratorTarget::DeviceLinkSetter setter(*target);
1445
1446
0
  cmComputeLinkInformation* pcli = target->GetLinkInformation(config);
1447
1448
0
  auto linklang = linkLineComputer.GetLinkerLanguage(target, config);
1449
0
  auto ipoEnabled = target->IsIPOEnabled(linklang, config);
1450
0
  if (!ipoEnabled && pcli) {
1451
0
    ipoEnabled = linkLineComputer.ComputeRequiresDeviceLinkingIPOFlag(*pcli);
1452
0
  }
1453
0
  if (ipoEnabled) {
1454
0
    if (cmValue cudaIPOFlags = this->Makefile->GetDefinition(
1455
0
          "CMAKE_CUDA_DEVICE_LINK_OPTIONS_IPO")) {
1456
0
      linkFlags += *cudaIPOFlags;
1457
0
    }
1458
0
  }
1459
1460
0
  if (pcli) {
1461
    // Compute the required device link libraries when
1462
    // resolving gpu lang device symbols
1463
0
    this->OutputLinkLibraries(pcli, &linkLineComputer, linkLibs, frameworkPath,
1464
0
                              linkPath);
1465
0
  }
1466
1467
0
  this->AddVisibilityPresetFlags(linkFlags, target, "CUDA");
1468
0
  this->GetGlobalGenerator()->EncodeLiteral(linkFlags);
1469
1470
0
  std::vector<std::string> linkOpts;
1471
0
  target->GetLinkOptions(linkOpts, config, "CUDA");
1472
0
  this->SetLinkScriptShell(this->GetGlobalGenerator()->GetUseLinkScript());
1473
  // LINK_OPTIONS are escaped.
1474
0
  this->AppendCompileOptions(linkFlags, linkOpts);
1475
0
  this->SetLinkScriptShell(false);
1476
0
}
1477
1478
void cmLocalGenerator::GetTargetFlags(
1479
  cmLinkLineComputer* linkLineComputer, std::string const& config,
1480
  std::string& linkLibs, std::string& flags, std::string& linkFlags,
1481
  std::string& frameworkPath, std::string& linkPath, cmGeneratorTarget* target)
1482
0
{
1483
0
  std::vector<BT<std::string>> linkFlagsList;
1484
0
  std::vector<BT<std::string>> linkPathList;
1485
0
  std::vector<BT<std::string>> linkLibsList;
1486
0
  this->GetTargetFlags(linkLineComputer, config, linkLibsList, flags,
1487
0
                       linkFlagsList, frameworkPath, linkPathList, target);
1488
0
  this->AppendFlags(linkFlags, linkFlagsList);
1489
0
  this->AppendFlags(linkPath, linkPathList);
1490
0
  this->AppendFlags(linkLibs, linkLibsList);
1491
0
}
1492
1493
void cmLocalGenerator::GetTargetFlags(
1494
  cmLinkLineComputer* linkLineComputer, std::string const& config,
1495
  std::vector<BT<std::string>>& linkLibs, std::string& flags,
1496
  std::vector<BT<std::string>>& linkFlags, std::string& frameworkPath,
1497
  std::vector<BT<std::string>>& linkPath, cmGeneratorTarget* target)
1498
0
{
1499
0
  std::string const configUpper = cmSystemTools::UpperCase(config);
1500
0
  cmComputeLinkInformation* pcli = target->GetLinkInformation(config);
1501
1502
0
  std::string const linkLanguage =
1503
0
    linkLineComputer->GetLinkerLanguage(target, config);
1504
1505
0
  {
1506
0
    std::string createFlags;
1507
0
    this->AppendTargetCreationLinkFlags(createFlags, target, linkLanguage);
1508
0
    if (!createFlags.empty()) {
1509
0
      linkFlags.emplace_back(std::move(createFlags));
1510
0
    }
1511
0
  }
1512
1513
0
  switch (target->GetType()) {
1514
0
    case cmStateEnums::STATIC_LIBRARY:
1515
0
      linkFlags = this->GetStaticLibraryFlags(config, linkLanguage, target);
1516
0
      break;
1517
0
    case cmStateEnums::MODULE_LIBRARY:
1518
0
      CM_FALLTHROUGH;
1519
0
    case cmStateEnums::SHARED_LIBRARY: {
1520
0
      if (this->IsSplitSwiftBuild() || linkLanguage != "Swift") {
1521
0
        std::string libFlags;
1522
0
        this->AddTargetTypeLinkerFlags(libFlags, target, linkLanguage, config);
1523
0
        if (!libFlags.empty()) {
1524
0
          linkFlags.emplace_back(std::move(libFlags));
1525
0
        }
1526
0
      }
1527
1528
0
      std::string langLinkFlags;
1529
0
      this->AddPerLanguageLinkFlags(langLinkFlags, target, linkLanguage,
1530
0
                                    config);
1531
0
      if (!langLinkFlags.empty()) {
1532
0
        linkFlags.emplace_back(std::move(langLinkFlags));
1533
0
      }
1534
1535
0
      std::string sharedLibFlags;
1536
0
      this->AddTargetPropertyLinkFlags(sharedLibFlags, target, config);
1537
0
      if (!sharedLibFlags.empty()) {
1538
0
        this->GetGlobalGenerator()->EncodeLiteral(sharedLibFlags);
1539
0
        linkFlags.emplace_back(std::move(sharedLibFlags));
1540
0
      }
1541
1542
0
      std::vector<BT<std::string>> linkOpts =
1543
0
        target->GetLinkOptions(config, linkLanguage);
1544
0
      this->SetLinkScriptShell(this->GetGlobalGenerator()->GetUseLinkScript());
1545
      // LINK_OPTIONS are escaped.
1546
0
      this->AppendCompileOptions(linkFlags, linkOpts);
1547
0
      this->SetLinkScriptShell(false);
1548
1549
0
      if (pcli) {
1550
0
        this->OutputLinkLibraries(pcli, linkLineComputer, linkLibs,
1551
0
                                  frameworkPath, linkPath);
1552
0
      }
1553
0
    } break;
1554
0
    case cmStateEnums::EXECUTABLE: {
1555
0
      if (linkLanguage != "Swift") {
1556
0
        std::string exeFlags;
1557
0
        this->AddTargetTypeLinkerFlags(exeFlags, target, linkLanguage, config);
1558
0
        if (!exeFlags.empty()) {
1559
0
          linkFlags.emplace_back(std::move(exeFlags));
1560
0
        }
1561
0
      }
1562
1563
0
      std::string langLinkFlags;
1564
0
      this->AddPerLanguageLinkFlags(langLinkFlags, target, linkLanguage,
1565
0
                                    config);
1566
0
      if (!langLinkFlags.empty()) {
1567
0
        linkFlags.emplace_back(std::move(langLinkFlags));
1568
0
      }
1569
1570
0
      {
1571
0
        auto exeType = cmStrCat(
1572
0
          "CMAKE_", linkLanguage, "_CREATE_",
1573
0
          (target->IsWin32Executable(config) ? "WIN32" : "CONSOLE"), "_EXE");
1574
0
        std::string exeFlags;
1575
0
        this->AppendFlags(exeFlags, this->Makefile->GetDefinition(exeType),
1576
0
                          exeType, target, cmBuildStep::Link, linkLanguage);
1577
0
        if (!exeFlags.empty()) {
1578
0
          linkFlags.emplace_back(std::move(exeFlags));
1579
0
        }
1580
0
      }
1581
1582
0
      std::string exeFlags;
1583
0
      if (target->IsExecutableWithExports()) {
1584
0
        exeFlags += this->Makefile->GetSafeDefinition(
1585
0
          cmStrCat("CMAKE_EXE_EXPORTS_", linkLanguage, "_FLAG"));
1586
0
        exeFlags += " ";
1587
0
      }
1588
1589
0
      this->AddLanguageFlagsForLinking(flags, target, linkLanguage, config);
1590
0
      if (pcli) {
1591
0
        this->OutputLinkLibraries(pcli, linkLineComputer, linkLibs,
1592
0
                                  frameworkPath, linkPath);
1593
0
      }
1594
1595
0
      if (this->Makefile->IsOn("BUILD_SHARED_LIBS")) {
1596
0
        std::string sFlagVar = "CMAKE_SHARED_BUILD_" + linkLanguage + "_FLAGS";
1597
0
        exeFlags += this->Makefile->GetSafeDefinition(sFlagVar);
1598
0
        exeFlags += " ";
1599
0
      }
1600
1601
0
      std::string exeExportFlags =
1602
0
        this->GetExeExportFlags(linkLanguage, *target);
1603
0
      if (!exeExportFlags.empty()) {
1604
0
        exeFlags += exeExportFlags;
1605
0
        exeFlags += " ";
1606
0
      }
1607
1608
0
      this->AddTargetPropertyLinkFlags(exeFlags, target, config);
1609
1610
0
      if (!exeFlags.empty()) {
1611
0
        this->GetGlobalGenerator()->EncodeLiteral(exeFlags);
1612
0
        linkFlags.emplace_back(std::move(exeFlags));
1613
0
      }
1614
1615
0
      std::vector<BT<std::string>> linkOpts =
1616
0
        target->GetLinkOptions(config, linkLanguage);
1617
0
      this->SetLinkScriptShell(this->GetGlobalGenerator()->GetUseLinkScript());
1618
      // LINK_OPTIONS are escaped.
1619
0
      this->AppendCompileOptions(linkFlags, linkOpts);
1620
0
      this->SetLinkScriptShell(false);
1621
0
    } break;
1622
0
    default:
1623
0
      break;
1624
0
  }
1625
1626
0
  std::string extraLinkFlags;
1627
0
  this->AppendLinkerTypeFlags(extraLinkFlags, target, config, linkLanguage);
1628
0
  this->AppendPositionIndependentLinkerFlags(extraLinkFlags, target, config,
1629
0
                                             linkLanguage);
1630
0
  this->AppendWarningAsErrorLinkerFlags(extraLinkFlags, target, linkLanguage);
1631
0
  this->AppendIPOLinkerFlags(extraLinkFlags, target, config, linkLanguage);
1632
0
  this->AppendModuleDefinitionFlag(extraLinkFlags, target, linkLineComputer,
1633
0
                                   config, linkLanguage);
1634
1635
0
  if (!extraLinkFlags.empty()) {
1636
0
    this->GetGlobalGenerator()->EncodeLiteral(extraLinkFlags);
1637
0
    linkFlags.emplace_back(std::move(extraLinkFlags));
1638
0
  }
1639
0
}
1640
1641
void cmLocalGenerator::GetTargetCompileFlags(cmGeneratorTarget* target,
1642
                                             std::string const& config,
1643
                                             std::string const& lang,
1644
                                             std::string& flags,
1645
                                             std::string const& arch)
1646
0
{
1647
0
  std::vector<BT<std::string>> tmpFlags =
1648
0
    this->GetTargetCompileFlags(target, config, lang, arch);
1649
0
  this->AppendFlags(flags, tmpFlags);
1650
0
}
1651
1652
std::vector<BT<std::string>> cmLocalGenerator::GetTargetCompileFlags(
1653
  cmGeneratorTarget* target, std::string const& config,
1654
  std::string const& lang, std::string const& arch)
1655
0
{
1656
0
  std::vector<BT<std::string>> flags;
1657
0
  std::string compileFlags;
1658
1659
0
  cmMakefile* mf = this->GetMakefile();
1660
1661
  // Add language-specific flags.
1662
0
  this->AddLanguageFlags(compileFlags, target, cmBuildStep::Compile, lang,
1663
0
                         config);
1664
1665
0
  if (target->IsIPOEnabled(lang, config)) {
1666
0
    this->AppendFeatureOptions(compileFlags, lang, "IPO");
1667
0
  }
1668
1669
0
  this->AddArchitectureFlags(compileFlags, target, lang, config, arch);
1670
1671
0
  if (lang == "Fortran") {
1672
0
    this->AppendFlags(compileFlags,
1673
0
                      this->GetTargetFortranFlags(target, config));
1674
0
  } else if (lang == "Swift") {
1675
    // Only set the compile mode if CMP0157 is set
1676
0
    if (cm::optional<cmSwiftCompileMode> swiftCompileMode =
1677
0
          this->GetSwiftCompileMode(target, config)) {
1678
0
      std::string swiftCompileModeFlag;
1679
0
      switch (*swiftCompileMode) {
1680
0
        case cmSwiftCompileMode::Incremental: {
1681
0
          swiftCompileModeFlag = "-incremental";
1682
0
          if (cmValue flag =
1683
0
                mf->GetDefinition("CMAKE_Swift_COMPILE_OPTIONS_INCREMENTAL")) {
1684
0
            swiftCompileModeFlag = *flag;
1685
0
          }
1686
0
          break;
1687
0
        }
1688
0
        case cmSwiftCompileMode::Wholemodule: {
1689
0
          swiftCompileModeFlag = "-wmo";
1690
0
          if (cmValue flag =
1691
0
                mf->GetDefinition("CMAKE_Swift_COMPILE_OPTIONS_WMO")) {
1692
0
            swiftCompileModeFlag = *flag;
1693
0
          }
1694
0
          break;
1695
0
        }
1696
0
        case cmSwiftCompileMode::Singlefile:
1697
0
          break;
1698
0
        case cmSwiftCompileMode::Unknown: {
1699
0
          this->IssueMessage(
1700
0
            MessageType::AUTHOR_WARNING,
1701
0
            cmStrCat("Unknown Swift_COMPILATION_MODE on target '",
1702
0
                     target->GetName(), '\''));
1703
0
        }
1704
0
      }
1705
0
      this->AppendFlags(compileFlags, swiftCompileModeFlag);
1706
0
    }
1707
0
  }
1708
1709
0
  this->AddFeatureFlags(compileFlags, target, lang, config);
1710
0
  this->AddVisibilityPresetFlags(compileFlags, target, lang);
1711
0
  this->AddColorDiagnosticsFlags(compileFlags, lang);
1712
0
  this->AppendFlags(compileFlags, mf->GetDefineFlags());
1713
0
  this->AppendFlags(compileFlags,
1714
0
                    this->GetFrameworkFlags(lang, config, target));
1715
0
  this->AppendFlags(compileFlags,
1716
0
                    this->GetXcFrameworkFlags(lang, config, target));
1717
1718
0
  if (!compileFlags.empty()) {
1719
0
    flags.emplace_back(std::move(compileFlags));
1720
0
  }
1721
0
  this->AddCompileOptions(flags, target, lang, config);
1722
0
  return flags;
1723
0
}
1724
1725
std::string cmLocalGenerator::GetFrameworkFlags(std::string const& lang,
1726
                                                std::string const& config,
1727
                                                cmGeneratorTarget* target)
1728
0
{
1729
0
  cmLocalGenerator* lg = target->GetLocalGenerator();
1730
0
  cmMakefile* mf = lg->GetMakefile();
1731
1732
0
  if (!target->IsApple()) {
1733
0
    return std::string();
1734
0
  }
1735
1736
0
  cmValue fwSearchFlag =
1737
0
    mf->GetDefinition(cmStrCat("CMAKE_", lang, "_FRAMEWORK_SEARCH_FLAG"));
1738
0
  cmValue sysFwSearchFlag = mf->GetDefinition(
1739
0
    cmStrCat("CMAKE_", lang, "_SYSTEM_FRAMEWORK_SEARCH_FLAG"));
1740
1741
0
  if (!fwSearchFlag && !sysFwSearchFlag) {
1742
0
    return std::string{};
1743
0
  }
1744
1745
0
  std::set<std::string> emitted;
1746
#ifdef __APPLE__ /* don't insert this when crosscompiling e.g. to iphone */
1747
  emitted.insert("/System/Library/Frameworks");
1748
#endif
1749
0
  std::vector<std::string> includes;
1750
1751
0
  lg->GetIncludeDirectories(includes, target, "C", config);
1752
  // check all include directories for frameworks as this
1753
  // will already have added a -F for the framework
1754
0
  for (std::string const& include : includes) {
1755
0
    if (lg->GetGlobalGenerator()->NameResolvesToFramework(include)) {
1756
0
      std::string frameworkDir = cmStrCat(include, "/../");
1757
0
      frameworkDir = cmSystemTools::CollapseFullPath(frameworkDir);
1758
0
      emitted.insert(frameworkDir);
1759
0
    }
1760
0
  }
1761
1762
0
  std::string flags;
1763
0
  if (cmComputeLinkInformation* cli = target->GetLinkInformation(config)) {
1764
0
    std::vector<std::string> const& frameworks = cli->GetFrameworkPaths();
1765
0
    for (std::string const& framework : frameworks) {
1766
0
      if (emitted.insert(framework).second) {
1767
0
        if (sysFwSearchFlag &&
1768
0
            target->IsSystemIncludeDirectory(framework, config, lang)) {
1769
0
          flags += *sysFwSearchFlag;
1770
0
        } else {
1771
0
          flags += *fwSearchFlag;
1772
0
        }
1773
0
        flags +=
1774
0
          lg->ConvertToOutputFormat(framework, cmOutputConverter::SHELL);
1775
0
        flags += " ";
1776
0
      }
1777
0
    }
1778
0
  }
1779
0
  return flags;
1780
0
}
1781
1782
std::string cmLocalGenerator::GetXcFrameworkFlags(std::string const& lang,
1783
                                                  std::string const& config,
1784
                                                  cmGeneratorTarget* target)
1785
0
{
1786
0
  cmLocalGenerator* lg = target->GetLocalGenerator();
1787
0
  cmMakefile* mf = lg->GetMakefile();
1788
1789
0
  if (!target->IsApple()) {
1790
0
    return std::string();
1791
0
  }
1792
1793
0
  cmValue includeSearchFlag =
1794
0
    mf->GetDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_", lang));
1795
0
  cmValue sysIncludeSearchFlag =
1796
0
    mf->GetDefinition(cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", lang));
1797
1798
0
  if (!includeSearchFlag && !sysIncludeSearchFlag) {
1799
0
    return std::string{};
1800
0
  }
1801
1802
0
  std::string flags;
1803
0
  if (cmComputeLinkInformation* cli = target->GetLinkInformation(config)) {
1804
0
    std::vector<std::string> const& paths = cli->GetXcFrameworkHeaderPaths();
1805
0
    for (std::string const& path : paths) {
1806
0
      if (sysIncludeSearchFlag &&
1807
0
          target->IsSystemIncludeDirectory(path, config, lang)) {
1808
0
        flags += *sysIncludeSearchFlag;
1809
0
      } else {
1810
0
        flags += *includeSearchFlag;
1811
0
      }
1812
0
      flags += lg->ConvertToOutputFormat(path, cmOutputConverter::SHELL);
1813
0
      flags += " ";
1814
0
    }
1815
0
  }
1816
0
  return flags;
1817
0
}
1818
1819
void cmLocalGenerator::GetTargetDefines(cmGeneratorTarget const* target,
1820
                                        std::string const& config,
1821
                                        std::string const& lang,
1822
                                        std::set<std::string>& defines) const
1823
0
{
1824
0
  std::set<BT<std::string>> tmp = this->GetTargetDefines(target, config, lang);
1825
0
  for (BT<std::string> const& v : tmp) {
1826
0
    defines.emplace(v.Value);
1827
0
  }
1828
0
}
1829
1830
std::set<BT<std::string>> cmLocalGenerator::GetTargetDefines(
1831
  cmGeneratorTarget const* target, std::string const& config,
1832
  std::string const& lang) const
1833
0
{
1834
0
  std::set<BT<std::string>> defines;
1835
1836
  // Add the export symbol definition for shared library objects.
1837
0
  if (std::string const* exportMacro = target->GetExportMacro()) {
1838
0
    this->AppendDefines(defines, *exportMacro);
1839
0
  }
1840
0
  for (auto const& sharedLibCompileDef :
1841
0
       target->GetSharedLibraryCompileDefs(config)) {
1842
0
    this->AppendDefines(defines, sharedLibCompileDef);
1843
0
  }
1844
1845
  // Add preprocessor definitions for this target and configuration.
1846
0
  std::vector<BT<std::string>> targetDefines =
1847
0
    target->GetCompileDefinitions(config, lang);
1848
0
  this->AppendDefines(defines, targetDefines);
1849
1850
0
  return defines;
1851
0
}
1852
1853
std::string cmLocalGenerator::GetTargetFortranFlags(
1854
  cmGeneratorTarget const* /*unused*/, std::string const& /*unused*/)
1855
0
{
1856
  // Implemented by specific generators that override this.
1857
0
  return std::string();
1858
0
}
1859
1860
/**
1861
 * Output the linking rules on a command line.  For executables,
1862
 * targetLibrary should be a NULL pointer.  For libraries, it should point
1863
 * to the name of the library.  This will not link a library against itself.
1864
 */
1865
void cmLocalGenerator::OutputLinkLibraries(
1866
  cmComputeLinkInformation* pcli, cmLinkLineComputer* linkLineComputer,
1867
  std::string& linkLibraries, std::string& frameworkPath,
1868
  std::string& linkPath)
1869
0
{
1870
0
  std::vector<BT<std::string>> linkLibrariesList;
1871
0
  std::vector<BT<std::string>> linkPathList;
1872
0
  this->OutputLinkLibraries(pcli, linkLineComputer, linkLibrariesList,
1873
0
                            frameworkPath, linkPathList);
1874
0
  pcli->AppendValues(linkLibraries, linkLibrariesList);
1875
0
  pcli->AppendValues(linkPath, linkPathList);
1876
0
}
1877
1878
void cmLocalGenerator::OutputLinkLibraries(
1879
  cmComputeLinkInformation* pcli, cmLinkLineComputer* linkLineComputer,
1880
  std::vector<BT<std::string>>& linkLibraries, std::string& frameworkPath,
1881
  std::vector<BT<std::string>>& linkPath)
1882
0
{
1883
0
  cmComputeLinkInformation& cli = *pcli;
1884
1885
0
  std::string linkLanguage = cli.GetLinkLanguage();
1886
1887
0
  std::string libPathFlag;
1888
0
  if (cmValue value = this->Makefile->GetDefinition(
1889
0
        "CMAKE_" + cli.GetLinkLanguage() + "_LIBRARY_PATH_FLAG")) {
1890
0
    libPathFlag = *value;
1891
0
  } else {
1892
0
    libPathFlag =
1893
0
      this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_FLAG");
1894
0
  }
1895
1896
0
  std::string libPathTerminator;
1897
0
  if (cmValue value = this->Makefile->GetDefinition(
1898
0
        "CMAKE_" + cli.GetLinkLanguage() + "_LIBRARY_PATH_TERMINATOR")) {
1899
0
    libPathTerminator = *value;
1900
0
  } else {
1901
0
    libPathTerminator =
1902
0
      this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_TERMINATOR");
1903
0
  }
1904
1905
  // Add standard link directories for this language
1906
0
  std::string stdLinkDirString = this->Makefile->GetSafeDefinition(
1907
0
    cmStrCat("CMAKE_", cli.GetLinkLanguage(), "_STANDARD_LINK_DIRECTORIES"));
1908
1909
  // Add standard libraries for this language.
1910
0
  std::string stdLibString = this->Makefile->GetSafeDefinition(
1911
0
    cmStrCat("CMAKE_", cli.GetLinkLanguage(), "_STANDARD_LIBRARIES"));
1912
1913
  // Append the framework search path flags.
1914
0
  cmValue fwSearchFlag = this->Makefile->GetDefinition(
1915
0
    cmStrCat("CMAKE_", linkLanguage, "_FRAMEWORK_SEARCH_FLAG"));
1916
1917
0
  frameworkPath = linkLineComputer->ComputeFrameworkPath(cli, fwSearchFlag);
1918
0
  linkLineComputer->ComputeLinkPath(cli, libPathFlag, libPathTerminator,
1919
0
                                    stdLinkDirString, linkPath);
1920
0
  linkLineComputer->ComputeLinkLibraries(cli, stdLibString, linkLibraries);
1921
0
}
1922
1923
std::string cmLocalGenerator::GetExeExportFlags(
1924
  std::string const& linkLanguage, cmGeneratorTarget& tgt) const
1925
0
{
1926
0
  std::string linkFlags;
1927
1928
  // Flags to export symbols from an executable.
1929
0
  if (tgt.GetType() == cmStateEnums::EXECUTABLE &&
1930
0
      this->StateSnapshot.GetState()->GetGlobalPropertyAsBool(
1931
0
        "TARGET_SUPPORTS_SHARED_LIBS")) {
1932
    // Only add the flags if ENABLE_EXPORTS is on,
1933
    // except on AIX where we compute symbol exports.
1934
0
    if (!tgt.IsAIX() && tgt.GetPropertyAsBool("ENABLE_EXPORTS")) {
1935
0
      linkFlags = this->Makefile->GetSafeDefinition(
1936
0
        cmStrCat("CMAKE_SHARED_LIBRARY_LINK_", linkLanguage, "_FLAGS"));
1937
0
    }
1938
0
  }
1939
0
  return linkFlags;
1940
0
}
1941
1942
bool cmLocalGenerator::AllAppleArchSysrootsAreTheSame(
1943
  std::vector<std::string> const& archs, cmValue sysroot)
1944
0
{
1945
0
  if (!sysroot) {
1946
0
    return false;
1947
0
  }
1948
1949
0
  return std::all_of(archs.begin(), archs.end(),
1950
0
                     [this, sysroot](std::string const& arch) -> bool {
1951
0
                       std::string const& archSysroot =
1952
0
                         this->AppleArchSysroots[arch];
1953
0
                       return cmIsOff(archSysroot) || *sysroot == archSysroot;
1954
0
                     });
1955
0
}
1956
1957
void cmLocalGenerator::AddArchitectureFlags(std::string& flags,
1958
                                            cmGeneratorTarget const* target,
1959
                                            std::string const& lang,
1960
                                            std::string const& config,
1961
                                            std::string const& filterArch)
1962
0
{
1963
  // Only add Apple specific flags on Apple platforms
1964
0
  if (target->IsApple() && this->EmitUniversalBinaryFlags) {
1965
0
    std::vector<std::string> archs = target->GetAppleArchs(config, lang);
1966
0
    if (!archs.empty() &&
1967
0
        (lang == "C" || lang == "CXX" || lang == "OBJC" || lang == "OBJCXX" ||
1968
0
         lang == "ASM")) {
1969
0
      for (std::string const& arch : archs) {
1970
0
        if (filterArch.empty() || filterArch == arch) {
1971
0
          flags += " -arch ";
1972
0
          flags += arch;
1973
0
        }
1974
0
      }
1975
0
    }
1976
1977
0
    cmValue sysroot = this->Makefile->GetDefinition("CMAKE_OSX_SYSROOT");
1978
0
    if (sysroot.IsEmpty() &&
1979
0
        this->Makefile->IsOn(
1980
0
          cmStrCat("CMAKE_", lang, "_COMPILER_APPLE_SYSROOT_REQUIRED"))) {
1981
0
      sysroot = this->Makefile->GetDefinition("_CMAKE_OSX_SYSROOT_PATH");
1982
0
    }
1983
0
    if (sysroot && *sysroot == "/") {
1984
0
      sysroot = nullptr;
1985
0
    }
1986
0
    std::string sysrootFlagVar = "CMAKE_" + lang + "_SYSROOT_FLAG";
1987
0
    cmValue sysrootFlag = this->Makefile->GetDefinition(sysrootFlagVar);
1988
0
    if (cmNonempty(sysrootFlag)) {
1989
0
      if (!this->AppleArchSysroots.empty() &&
1990
0
          !this->AllAppleArchSysrootsAreTheSame(archs, sysroot)) {
1991
0
        for (std::string const& arch : archs) {
1992
0
          std::string const& archSysroot = this->AppleArchSysroots[arch];
1993
0
          if (cmIsOff(archSysroot)) {
1994
0
            continue;
1995
0
          }
1996
0
          if (filterArch.empty() || filterArch == arch) {
1997
0
            flags += " -Xarch_" + arch + " ";
1998
            // Combine sysroot flag and path to work with -Xarch
1999
0
            std::string arch_sysroot = *sysrootFlag + archSysroot;
2000
0
            flags += this->ConvertToOutputFormat(arch_sysroot, SHELL);
2001
0
          }
2002
0
        }
2003
0
      } else if (cmNonempty(sysroot)) {
2004
0
        flags += " ";
2005
0
        flags += *sysrootFlag;
2006
0
        flags += " ";
2007
0
        flags += this->ConvertToOutputFormat(*sysroot, SHELL);
2008
0
      }
2009
0
    }
2010
2011
0
    cmValue deploymentTarget =
2012
0
      this->Makefile->GetDefinition("CMAKE_OSX_DEPLOYMENT_TARGET");
2013
0
    if (cmNonempty(deploymentTarget)) {
2014
0
      std::string deploymentTargetFlagVar =
2015
0
        "CMAKE_" + lang + "_OSX_DEPLOYMENT_TARGET_FLAG";
2016
0
      cmValue deploymentTargetFlag =
2017
0
        this->Makefile->GetDefinition(deploymentTargetFlagVar);
2018
0
      if (cmNonempty(deploymentTargetFlag) &&
2019
          // CMAKE_<LANG>_COMPILER_TARGET overrides a --target= for
2020
          // CMAKE_OSX_DEPLOYMENT_TARGET, e.g., for visionOS.
2021
0
          (!cmHasLiteralPrefix(*deploymentTarget, "--target=") ||
2022
0
           this->Makefile
2023
0
             ->GetDefinition(cmStrCat("CMAKE_", lang, "_COMPILER_TARGET"))
2024
0
             .IsEmpty())) {
2025
0
        std::string flag = *deploymentTargetFlag;
2026
2027
        // Add the deployment target architecture to the flag, if needed.
2028
0
        static std::string const kARCH = "<ARCH>";
2029
0
        std::string::size_type archPos = flag.find(kARCH);
2030
0
        if (archPos != std::string::npos) {
2031
          // This placeholder is meant for visionOS, so default to arm64
2032
          // unless only non-arm64 archs are given.
2033
0
          std::string const arch =
2034
0
            (archs.empty() || cm::contains(archs, "arm64")) ? "arm64"
2035
0
                                                            : archs[0];
2036
          // Replace the placeholder with its value.
2037
0
          flag = cmStrCat(flag.substr(0, archPos), arch,
2038
0
                          flag.substr(archPos + kARCH.size()));
2039
0
        }
2040
2041
        // Add the deployment target version to the flag.
2042
0
        static std::string const kVERSION_MIN = "<VERSION_MIN>";
2043
0
        std::string::size_type verPos = flag.find(kVERSION_MIN);
2044
0
        if (verPos != std::string::npos) {
2045
          // Replace the placeholder with its value.
2046
0
          flag = cmStrCat(flag.substr(0, verPos), *deploymentTarget,
2047
0
                          flag.substr(verPos + kVERSION_MIN.size()));
2048
0
        } else {
2049
          // There is no placeholder, so append the value.
2050
0
          flag = cmStrCat(flag, *deploymentTarget);
2051
0
        }
2052
2053
0
        flags += " ";
2054
0
        flags += flag;
2055
0
      }
2056
0
    }
2057
0
  }
2058
0
}
2059
2060
void cmLocalGenerator::AddLanguageFlags(std::string& flags,
2061
                                        cmGeneratorTarget const* target,
2062
                                        cmBuildStep compileOrLink,
2063
                                        std::string const& lang,
2064
                                        std::string const& config)
2065
0
{
2066
  // Add language-specific flags.
2067
0
  this->AddConfigVariableFlags(flags, cmStrCat("CMAKE_", lang, "_FLAGS"),
2068
0
                               config);
2069
2070
  // Add the language standard flag for compiling, and sometimes linking.
2071
0
  if (compileOrLink == cmBuildStep::Compile ||
2072
0
      (compileOrLink == cmBuildStep::Link &&
2073
       // Some toolchains require use of the language standard flag
2074
       // when linking in order to use the matching standard library.
2075
       // FIXME: If CMake gains an abstraction for standard library
2076
       // selection, this will have to be reconciled with it.
2077
0
       this->Makefile->IsOn(
2078
0
         cmStrCat("CMAKE_", lang, "_LINK_WITH_STANDARD_COMPILE_OPTION")))) {
2079
0
    cmStandardLevelResolver standardResolver(this->Makefile);
2080
0
    std::string const& optionFlagDef =
2081
0
      standardResolver.GetCompileOptionDef(target, lang, config);
2082
0
    if (!optionFlagDef.empty()) {
2083
0
      cmValue opt =
2084
0
        target->Target->GetMakefile()->GetDefinition(optionFlagDef);
2085
0
      if (opt) {
2086
0
        cmList optList{ *opt };
2087
0
        for (std::string const& i : optList) {
2088
0
          this->AppendFlagEscape(flags, i);
2089
0
        }
2090
0
      }
2091
0
    }
2092
0
  }
2093
2094
0
  std::string compilerId = this->Makefile->GetSafeDefinition(
2095
0
    cmStrCat("CMAKE_", lang, "_COMPILER_ID"));
2096
2097
0
  std::string compilerSimulateId = this->Makefile->GetSafeDefinition(
2098
0
    cmStrCat("CMAKE_", lang, "_SIMULATE_ID"));
2099
2100
0
  bool const compilerTargetsMsvcABI =
2101
0
    (compilerId == "MSVC" || compilerSimulateId == "MSVC");
2102
0
  bool const compilerTargetsWatcomABI =
2103
0
    (compilerId == "OpenWatcom" || compilerSimulateId == "OpenWatcom");
2104
2105
0
  if (lang == "Swift") {
2106
0
    if (cmValue v = target->GetProperty("Swift_LANGUAGE_VERSION")) {
2107
0
      if (cmSystemTools::VersionCompare(
2108
0
            cmSystemTools::OP_GREATER_EQUAL,
2109
0
            this->Makefile->GetDefinition("CMAKE_Swift_COMPILER_VERSION"),
2110
0
            "4.2")) {
2111
0
        this->AppendFlags(flags, "-swift-version " + *v);
2112
0
      }
2113
0
    }
2114
0
  } else if (lang == "CUDA") {
2115
0
    target->AddCUDAArchitectureFlags(compileOrLink, config, flags);
2116
0
    target->AddCUDAToolkitFlags(flags);
2117
0
  } else if (lang == "ISPC") {
2118
0
    target->AddISPCTargetFlags(flags);
2119
0
  } else if (lang == "RC" &&
2120
0
             this->Makefile->GetSafeDefinition("CMAKE_RC_COMPILER")
2121
0
                 .find("llvm-rc") != std::string::npos) {
2122
0
    compilerId = this->Makefile->GetSafeDefinition("CMAKE_C_COMPILER_ID");
2123
0
    if (!compilerId.empty()) {
2124
0
      compilerSimulateId =
2125
0
        this->Makefile->GetSafeDefinition("CMAKE_C_SIMULATE_ID");
2126
0
    } else {
2127
0
      compilerId = this->Makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_ID");
2128
0
      compilerSimulateId =
2129
0
        this->Makefile->GetSafeDefinition("CMAKE_CXX_SIMULATE_ID");
2130
0
    }
2131
0
  } else if (lang == "HIP") {
2132
0
    target->AddHIPArchitectureFlags(compileOrLink, config, flags);
2133
0
  }
2134
2135
  // Add VFS Overlay for Clang compilers
2136
0
  if (compilerId == "Clang") {
2137
0
    if (cmValue vfsOverlay =
2138
0
          this->Makefile->GetDefinition("CMAKE_CLANG_VFS_OVERLAY")) {
2139
0
      if (compilerSimulateId == "MSVC") {
2140
0
        this->AppendCompileOptions(
2141
0
          flags,
2142
0
          std::vector<std::string>{ "-Xclang", "-ivfsoverlay", "-Xclang",
2143
0
                                    *vfsOverlay });
2144
0
      } else {
2145
0
        this->AppendCompileOptions(
2146
0
          flags, std::vector<std::string>{ "-ivfsoverlay", *vfsOverlay });
2147
0
      }
2148
0
    }
2149
0
  }
2150
  // Add MSVC runtime library flags.  This is activated by the presence
2151
  // of a default selection whether or not it is overridden by a property.
2152
0
  cmValue msvcRuntimeLibraryDefault =
2153
0
    this->Makefile->GetDefinition("CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT");
2154
0
  if (cmNonempty(msvcRuntimeLibraryDefault)) {
2155
0
    cmValue msvcRuntimeLibraryValue =
2156
0
      target->GetProperty("MSVC_RUNTIME_LIBRARY");
2157
0
    if (!msvcRuntimeLibraryValue) {
2158
0
      msvcRuntimeLibraryValue = msvcRuntimeLibraryDefault;
2159
0
    }
2160
0
    std::string const msvcRuntimeLibrary = cmGeneratorExpression::Evaluate(
2161
0
      *msvcRuntimeLibraryValue, this, config, target);
2162
0
    if (!msvcRuntimeLibrary.empty()) {
2163
0
      if (cmValue msvcRuntimeLibraryOptions = this->Makefile->GetDefinition(
2164
0
            "CMAKE_" + lang + "_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_" +
2165
0
            msvcRuntimeLibrary)) {
2166
0
        this->AppendCompileOptions(flags, *msvcRuntimeLibraryOptions);
2167
0
      } else if (compilerTargetsMsvcABI &&
2168
0
                 !cmSystemTools::GetErrorOccurredFlag()) {
2169
        // The compiler uses the MSVC ABI so it needs a known runtime library.
2170
0
        this->IssueMessage(MessageType::FATAL_ERROR,
2171
0
                           "MSVC_RUNTIME_LIBRARY value '" +
2172
0
                             msvcRuntimeLibrary + "' not known for this " +
2173
0
                             lang + " compiler.");
2174
0
      }
2175
0
    }
2176
0
  }
2177
2178
  // Add Watcom runtime library flags.  This is activated by the presence
2179
  // of a default selection whether or not it is overridden by a property.
2180
0
  cmValue watcomRuntimeLibraryDefault =
2181
0
    this->Makefile->GetDefinition("CMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT");
2182
0
  if (cmNonempty(watcomRuntimeLibraryDefault)) {
2183
0
    cmValue watcomRuntimeLibraryValue =
2184
0
      target->GetProperty("WATCOM_RUNTIME_LIBRARY");
2185
0
    if (!watcomRuntimeLibraryValue) {
2186
0
      watcomRuntimeLibraryValue = watcomRuntimeLibraryDefault;
2187
0
    }
2188
0
    std::string const watcomRuntimeLibrary = cmGeneratorExpression::Evaluate(
2189
0
      *watcomRuntimeLibraryValue, this, config, target);
2190
0
    if (!watcomRuntimeLibrary.empty()) {
2191
0
      if (cmValue watcomRuntimeLibraryOptions = this->Makefile->GetDefinition(
2192
0
            "CMAKE_" + lang + "_COMPILE_OPTIONS_WATCOM_RUNTIME_LIBRARY_" +
2193
0
            watcomRuntimeLibrary)) {
2194
0
        this->AppendCompileOptions(flags, *watcomRuntimeLibraryOptions);
2195
0
      } else if (compilerTargetsWatcomABI &&
2196
0
                 !cmSystemTools::GetErrorOccurredFlag()) {
2197
        // The compiler uses the Watcom ABI so it needs a known runtime
2198
        // library.
2199
0
        this->IssueMessage(MessageType::FATAL_ERROR,
2200
0
                           "WATCOM_RUNTIME_LIBRARY value '" +
2201
0
                             watcomRuntimeLibrary + "' not known for this " +
2202
0
                             lang + " compiler.");
2203
0
      }
2204
0
    }
2205
0
  }
2206
2207
  // Add MSVC runtime checks flags. This is activated by the presence
2208
  // of a default selection whether or not it is overridden by a property.
2209
0
  cmValue msvcRuntimeChecksDefault =
2210
0
    this->Makefile->GetDefinition("CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT");
2211
0
  if (cmNonempty(msvcRuntimeChecksDefault)) {
2212
0
    cmValue msvcRuntimeChecksValue =
2213
0
      target->GetProperty("MSVC_RUNTIME_CHECKS");
2214
0
    if (!msvcRuntimeChecksValue) {
2215
0
      msvcRuntimeChecksValue = msvcRuntimeChecksDefault;
2216
0
    }
2217
0
    cmList msvcRuntimeChecksList = cmGeneratorExpression::Evaluate(
2218
0
      *msvcRuntimeChecksValue, this, config, target);
2219
0
    msvcRuntimeChecksList.remove_duplicates();
2220
2221
    // RTC1/RTCsu VS GUI workaround
2222
0
    std::string const stackFrameErrorCheck = "StackFrameErrorCheck";
2223
0
    std::string const unitinitializedVariable = "UninitializedVariable";
2224
0
    std::string const rtcSU = "RTCsu";
2225
0
    if ((cm::contains(msvcRuntimeChecksList, stackFrameErrorCheck) &&
2226
0
         cm::contains(msvcRuntimeChecksList, unitinitializedVariable)) ||
2227
0
        cm::contains(msvcRuntimeChecksList, rtcSU)) {
2228
0
      msvcRuntimeChecksList.remove_items(
2229
0
        { stackFrameErrorCheck, unitinitializedVariable, rtcSU });
2230
0
      msvcRuntimeChecksList.append(rtcSU);
2231
0
    }
2232
2233
0
    for (std::string const& msvcRuntimeChecks : msvcRuntimeChecksList) {
2234
0
      if (cmValue msvcRuntimeChecksOptions =
2235
0
            this->Makefile->GetDefinition(cmStrCat(
2236
0
              "CMAKE_", lang,
2237
0
              "_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_" + msvcRuntimeChecks))) {
2238
0
        this->AppendCompileOptions(flags, *msvcRuntimeChecksOptions);
2239
0
      } else if (compilerTargetsMsvcABI &&
2240
0
                 !cmSystemTools::GetErrorOccurredFlag()) {
2241
        // The compiler uses the MSVC ABI so it needs a known runtime checks.
2242
0
        this->IssueMessage(MessageType::FATAL_ERROR,
2243
0
                           cmStrCat("MSVC_RUNTIME_CHECKS value '",
2244
0
                                    msvcRuntimeChecks, "' not known for this ",
2245
0
                                    lang, " compiler."));
2246
0
      }
2247
0
    }
2248
0
  }
2249
2250
  // Add MSVC debug information format flags if CMP0141 is NEW.
2251
0
  if (cm::optional<std::string> msvcDebugInformationFormat =
2252
0
        this->GetMSVCDebugFormatName(config, target)) {
2253
0
    if (!msvcDebugInformationFormat->empty()) {
2254
0
      if (cmValue msvcDebugInformationFormatOptions =
2255
0
            this->Makefile->GetDefinition(
2256
0
              cmStrCat("CMAKE_", lang,
2257
0
                       "_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_",
2258
0
                       *msvcDebugInformationFormat))) {
2259
0
        this->AppendCompileOptions(flags, *msvcDebugInformationFormatOptions);
2260
0
      } else if (compilerTargetsMsvcABI &&
2261
0
                 !cmSystemTools::GetErrorOccurredFlag()) {
2262
        // The compiler uses the MSVC ABI so it needs a known runtime library.
2263
0
        this->IssueMessage(MessageType::FATAL_ERROR,
2264
0
                           cmStrCat("MSVC_DEBUG_INFORMATION_FORMAT value '",
2265
0
                                    *msvcDebugInformationFormat,
2266
0
                                    "' not known for this ", lang,
2267
0
                                    " compiler."));
2268
0
      }
2269
0
    }
2270
0
  }
2271
0
}
2272
2273
void cmLocalGenerator::AddLanguageFlagsForLinking(
2274
  std::string& flags, cmGeneratorTarget const* target, std::string const& lang,
2275
  std::string const& config)
2276
0
{
2277
0
  this->AddLanguageFlags(flags, target, cmBuildStep::Link, lang, config);
2278
2279
0
  if (target->IsIPOEnabled(lang, config)) {
2280
0
    this->AppendFeatureOptions(flags, lang, "IPO");
2281
0
  }
2282
0
}
2283
2284
cmGeneratorTarget* cmLocalGenerator::FindGeneratorTargetToUse(
2285
  std::string const& name) const
2286
0
{
2287
0
  auto imported = this->ImportedGeneratorTargets.find(name);
2288
0
  if (imported != this->ImportedGeneratorTargets.end()) {
2289
0
    return imported->second;
2290
0
  }
2291
2292
  // find local alias to imported target
2293
0
  auto aliased = this->AliasTargets.find(name);
2294
0
  if (aliased != this->AliasTargets.end()) {
2295
0
    imported = this->ImportedGeneratorTargets.find(aliased->second);
2296
0
    if (imported != this->ImportedGeneratorTargets.end()) {
2297
0
      return imported->second;
2298
0
    }
2299
0
  }
2300
2301
0
  if (cmGeneratorTarget* t = this->FindLocalNonAliasGeneratorTarget(name)) {
2302
0
    return t;
2303
0
  }
2304
2305
0
  return this->GetGlobalGenerator()->FindGeneratorTarget(name);
2306
0
}
2307
2308
bool cmLocalGenerator::GetRealDependency(std::string const& inName,
2309
                                         std::string const& config,
2310
                                         std::string& dep)
2311
0
{
2312
  // Older CMake code may specify the dependency using the target
2313
  // output file rather than the target name.  Such code would have
2314
  // been written before there was support for target properties that
2315
  // modify the name so stripping down to just the file name should
2316
  // produce the target name in this case.
2317
0
  std::string name = cmSystemTools::GetFilenameName(inName);
2318
2319
  // If the input name is the empty string, there is no real
2320
  // dependency. Short-circuit the other checks:
2321
0
  if (name.empty()) {
2322
0
    return false;
2323
0
  }
2324
0
  if (cmHasSuffix(name, ".exe"_s)) {
2325
0
    name = cmSystemTools::GetFilenameWithoutLastExtension(name);
2326
0
  }
2327
2328
  // Look for a CMake target with the given name.
2329
0
  if (cmGeneratorTarget* target = this->FindGeneratorTargetToUse(name)) {
2330
    // make sure it is not just a coincidence that the target name
2331
    // found is part of the inName
2332
0
    if (cmSystemTools::FileIsFullPath(inName)) {
2333
0
      std::string tLocation;
2334
0
      if (target->GetType() >= cmStateEnums::EXECUTABLE &&
2335
0
          target->GetType() <= cmStateEnums::MODULE_LIBRARY) {
2336
0
        tLocation = target->GetLocation(config);
2337
0
        tLocation = cmSystemTools::GetFilenamePath(tLocation);
2338
0
        tLocation = cmSystemTools::CollapseFullPath(tLocation);
2339
0
      }
2340
0
      std::string depLocation =
2341
0
        cmSystemTools::GetFilenamePath(std::string(inName));
2342
0
      depLocation = cmSystemTools::CollapseFullPath(depLocation);
2343
0
      if (depLocation != tLocation) {
2344
        // it is a full path to a depend that has the same name
2345
        // as a target but is in a different location so do not use
2346
        // the target as the depend
2347
0
        dep = inName;
2348
0
        return true;
2349
0
      }
2350
0
    }
2351
0
    switch (target->GetType()) {
2352
0
      case cmStateEnums::EXECUTABLE:
2353
0
      case cmStateEnums::STATIC_LIBRARY:
2354
0
      case cmStateEnums::SHARED_LIBRARY:
2355
0
      case cmStateEnums::MODULE_LIBRARY:
2356
0
      case cmStateEnums::UNKNOWN_LIBRARY:
2357
0
        dep = target->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact,
2358
0
                                  /*realname=*/true);
2359
0
        return true;
2360
0
      case cmStateEnums::OBJECT_LIBRARY:
2361
        // An object library has no single file on which to depend.
2362
        // This was listed to get the target-level dependency.
2363
0
      case cmStateEnums::INTERFACE_LIBRARY:
2364
        // An interface library has no file on which to depend.
2365
        // This was listed to get the target-level dependency.
2366
0
      case cmStateEnums::UTILITY:
2367
0
      case cmStateEnums::GLOBAL_TARGET:
2368
        // A utility target has no file on which to depend.  This was listed
2369
        // only to get the target-level dependency.
2370
0
        return false;
2371
0
    }
2372
0
  }
2373
2374
  // The name was not that of a CMake target.  It must name a file.
2375
0
  if (cmSystemTools::FileIsFullPath(inName)) {
2376
    // This is a full path.  Return it as given.
2377
0
    dep = inName;
2378
0
    return true;
2379
0
  }
2380
2381
  // Check for a source file in this directory that matches the
2382
  // dependency.
2383
0
  if (cmSourceFile* sf = this->Makefile->GetSource(inName)) {
2384
0
    dep = sf->ResolveFullPath();
2385
0
    return true;
2386
0
  }
2387
2388
  // Treat the name as relative to the source directory in which it
2389
  // was given.
2390
0
  dep = cmStrCat(this->GetCurrentSourceDirectory(), '/', inName);
2391
2392
  // If the in-source path does not exist, assume it instead lives in the
2393
  // binary directory.
2394
0
  if (!cmSystemTools::FileExists(dep)) {
2395
0
    dep = cmStrCat(this->GetCurrentBinaryDirectory(), '/', inName);
2396
0
  }
2397
2398
0
  dep = cmSystemTools::CollapseFullPath(dep);
2399
2400
0
  return true;
2401
0
}
2402
2403
static void AddVisibilityCompileOption(std::string& flags,
2404
                                       cmGeneratorTarget const* target,
2405
                                       cmLocalGenerator* lg,
2406
                                       std::string const& lang)
2407
0
{
2408
0
  std::string compileOption = "CMAKE_" + lang + "_COMPILE_OPTIONS_VISIBILITY";
2409
0
  cmValue opt = lg->GetMakefile()->GetDefinition(compileOption);
2410
0
  if (!opt) {
2411
0
    return;
2412
0
  }
2413
0
  std::string flagDefine = lang + "_VISIBILITY_PRESET";
2414
2415
0
  cmValue prop = target->GetProperty(flagDefine);
2416
0
  if (!prop) {
2417
0
    return;
2418
0
  }
2419
0
  if ((*prop != "hidden") && (*prop != "default") && (*prop != "protected") &&
2420
0
      (*prop != "internal")) {
2421
0
    std::ostringstream e;
2422
0
    e << "Target " << target->GetName() << " uses unsupported value \""
2423
0
      << *prop << "\" for " << flagDefine << "."
2424
0
      << " The supported values are: default, hidden, protected, and "
2425
0
         "internal.";
2426
0
    cmSystemTools::Error(e.str());
2427
0
    return;
2428
0
  }
2429
0
  std::string option = *opt + *prop;
2430
0
  lg->AppendFlags(flags, option);
2431
0
}
2432
2433
static void AddInlineVisibilityCompileOption(std::string& flags,
2434
                                             cmGeneratorTarget const* target,
2435
                                             cmLocalGenerator* lg,
2436
                                             std::string const& lang)
2437
0
{
2438
0
  std::string compileOption =
2439
0
    cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN");
2440
0
  cmValue opt = lg->GetMakefile()->GetDefinition(compileOption);
2441
0
  if (!opt) {
2442
0
    return;
2443
0
  }
2444
2445
0
  bool prop = target->GetPropertyAsBool("VISIBILITY_INLINES_HIDDEN");
2446
0
  if (!prop) {
2447
0
    return;
2448
0
  }
2449
0
  lg->AppendFlags(flags, *opt);
2450
0
}
2451
2452
void cmLocalGenerator::AddVisibilityPresetFlags(
2453
  std::string& flags, cmGeneratorTarget const* target, std::string const& lang)
2454
0
{
2455
0
  if (lang.empty()) {
2456
0
    return;
2457
0
  }
2458
2459
0
  AddVisibilityCompileOption(flags, target, this, lang);
2460
2461
0
  if (lang == "CXX" || lang == "OBJCXX") {
2462
0
    AddInlineVisibilityCompileOption(flags, target, this, lang);
2463
0
  }
2464
0
}
2465
2466
void cmLocalGenerator::AddFeatureFlags(std::string& flags,
2467
                                       cmGeneratorTarget const* target,
2468
                                       std::string const& lang,
2469
                                       std::string const& config)
2470
0
{
2471
0
  int targetType = target->GetType();
2472
2473
0
  bool shared = ((targetType == cmStateEnums::SHARED_LIBRARY) ||
2474
0
                 (targetType == cmStateEnums::MODULE_LIBRARY));
2475
2476
0
  if (target->GetLinkInterfaceDependentBoolProperty(
2477
0
        "POSITION_INDEPENDENT_CODE", config)) {
2478
0
    this->AddPositionIndependentFlags(flags, lang, targetType);
2479
0
  }
2480
0
  if (shared) {
2481
0
    this->AppendFeatureOptions(flags, lang, "DLL");
2482
0
  }
2483
0
}
2484
2485
void cmLocalGenerator::AddPositionIndependentFlags(std::string& flags,
2486
                                                   std::string const& lang,
2487
                                                   int targetType)
2488
0
{
2489
0
  std::string picFlags;
2490
2491
0
  if (targetType == cmStateEnums::EXECUTABLE) {
2492
0
    picFlags = this->Makefile->GetSafeDefinition(
2493
0
      cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_PIE"));
2494
0
  }
2495
0
  if (picFlags.empty()) {
2496
0
    picFlags = this->Makefile->GetSafeDefinition(
2497
0
      cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_PIC"));
2498
0
  }
2499
0
  if (!picFlags.empty()) {
2500
0
    cmList options{ picFlags };
2501
0
    for (std::string const& o : options) {
2502
0
      this->AppendFlagEscape(flags, o);
2503
0
    }
2504
0
  }
2505
0
}
2506
2507
void cmLocalGenerator::AddColorDiagnosticsFlags(std::string& flags,
2508
                                                std::string const& lang)
2509
0
{
2510
0
  cmValue diag = this->Makefile->GetDefinition("CMAKE_COLOR_DIAGNOSTICS");
2511
0
  if (diag.IsSet()) {
2512
0
    std::string colorFlagName;
2513
0
    if (diag.IsOn()) {
2514
0
      colorFlagName =
2515
0
        cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_COLOR_DIAGNOSTICS");
2516
0
    } else {
2517
0
      colorFlagName =
2518
0
        cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_COLOR_DIAGNOSTICS_OFF");
2519
0
    }
2520
2521
0
    cmList options{ this->Makefile->GetDefinition(colorFlagName) };
2522
2523
0
    for (auto const& option : options) {
2524
0
      this->AppendFlagEscape(flags, option);
2525
0
    }
2526
0
  }
2527
0
}
2528
2529
void cmLocalGenerator::AddConfigVariableFlags(std::string& flags,
2530
                                              std::string const& var,
2531
                                              std::string const& config)
2532
0
{
2533
  // Add the flags from the variable itself.
2534
0
  this->AppendFlags(flags, this->Makefile->GetSafeDefinition(var));
2535
  // Add the flags from the build-type specific variable.
2536
0
  if (!config.empty()) {
2537
0
    std::string const flagsVar =
2538
0
      cmStrCat(var, '_', cmSystemTools::UpperCase(config));
2539
0
    this->AppendFlags(flags, this->Makefile->GetSafeDefinition(flagsVar));
2540
0
  }
2541
0
}
2542
void cmLocalGenerator::AddConfigVariableFlags(std::string& flags,
2543
                                              std::string const& var,
2544
                                              cmGeneratorTarget const* target,
2545
                                              cmBuildStep compileOrLink,
2546
                                              std::string const& lang,
2547
                                              std::string const& config)
2548
0
{
2549
0
  std::string newFlags;
2550
0
  this->AddConfigVariableFlags(newFlags, var, config);
2551
0
  if (!newFlags.empty()) {
2552
0
    this->AppendFlags(flags, newFlags, var, target, compileOrLink, lang);
2553
0
  }
2554
0
}
2555
2556
void cmLocalGenerator::AppendFlags(std::string& flags,
2557
                                   std::string const& newFlags) const
2558
0
{
2559
0
  bool allSpaces = std::all_of(newFlags.begin(), newFlags.end(), cmIsSpace);
2560
2561
0
  if (!newFlags.empty() && !allSpaces) {
2562
0
    if (!flags.empty()) {
2563
0
      flags += " ";
2564
0
    }
2565
0
    flags += newFlags;
2566
0
  }
2567
0
}
2568
2569
void cmLocalGenerator::AppendFlags(
2570
  std::string& flags, std::vector<BT<std::string>> const& newFlags) const
2571
0
{
2572
0
  for (BT<std::string> const& flag : newFlags) {
2573
0
    this->AppendFlags(flags, flag.Value);
2574
0
  }
2575
0
}
2576
2577
void cmLocalGenerator::AppendFlagEscape(std::string& flags,
2578
                                        std::string const& rawFlag) const
2579
0
{
2580
0
  this->AppendFlags(
2581
0
    flags,
2582
0
    this->EscapeForShell(rawFlag, false, false, false, this->IsNinjaMulti()));
2583
0
}
2584
2585
void cmLocalGenerator::AppendLinkFlagsWithParsing(
2586
  std::string& flags, std::string const& newFlags,
2587
  cmGeneratorTarget const* target, std::string const& language)
2588
0
{
2589
0
  std::vector<std::string> options;
2590
0
  cmSystemTools::ParseUnixCommandLine(newFlags.c_str(), options);
2591
0
  this->SetLinkScriptShell(this->GlobalGenerator->GetUseLinkScript());
2592
0
  std::vector<BT<std::string>> optionsWithBT{ options.size() };
2593
0
  std::transform(options.cbegin(), options.cend(), optionsWithBT.begin(),
2594
0
                 [](std::string const& item) -> BT<std::string> {
2595
0
                   return BT<std::string>{ item };
2596
0
                 });
2597
0
  target->ResolveLinkerWrapper(optionsWithBT, language);
2598
0
  for (auto const& item : optionsWithBT) {
2599
0
    this->AppendFlagEscape(flags, item.Value);
2600
0
  }
2601
0
  this->SetLinkScriptShell(false);
2602
0
}
2603
2604
void cmLocalGenerator::AppendFlags(std::string& flags,
2605
                                   std::string const& newFlags,
2606
                                   std::string const& name,
2607
                                   cmGeneratorTarget const* target,
2608
                                   cmBuildStep compileOrLink,
2609
                                   std::string const& language)
2610
0
{
2611
0
  switch (target->GetPolicyStatusCMP0181()) {
2612
0
    case cmPolicies::WARN:
2613
0
      if (!this->Makefile->GetCMakeInstance()->GetIsInTryCompile() &&
2614
0
          this->Makefile->PolicyOptionalWarningEnabled(
2615
0
            "CMAKE_POLICY_WARNING_CMP0181")) {
2616
0
        this->Makefile->GetCMakeInstance()->IssueMessage(
2617
0
          MessageType::AUTHOR_WARNING,
2618
0
          cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0181),
2619
0
                   "\nSince the policy is not set, the contents of variable '",
2620
0
                   name,
2621
0
                   "' will "
2622
0
                   "be used as is."),
2623
0
          target->GetBacktrace());
2624
0
      }
2625
0
      CM_FALLTHROUGH;
2626
0
    case cmPolicies::OLD:
2627
0
      this->AppendFlags(
2628
0
        flags, this->GetGlobalGenerator()->GetEncodedLiteral(newFlags));
2629
0
      break;
2630
0
    case cmPolicies::NEW:
2631
0
      if (compileOrLink == cmBuildStep::Link) {
2632
0
        this->AppendLinkFlagsWithParsing(flags, newFlags, target, language);
2633
0
      } else {
2634
0
        this->AppendFlags(flags, newFlags);
2635
0
      }
2636
0
  }
2637
0
}
2638
2639
void cmLocalGenerator::AddISPCDependencies(cmGeneratorTarget* target)
2640
0
{
2641
0
  std::vector<std::string> enabledLanguages =
2642
0
    this->GetState()->GetEnabledLanguages();
2643
0
  if (std::find(enabledLanguages.begin(), enabledLanguages.end(), "ISPC") ==
2644
0
      enabledLanguages.end()) {
2645
0
    return;
2646
0
  }
2647
2648
0
  cmValue ispcHeaderSuffixProp = target->GetProperty("ISPC_HEADER_SUFFIX");
2649
0
  assert(ispcHeaderSuffixProp);
2650
2651
0
  std::vector<std::string> ispcArchSuffixes =
2652
0
    detail::ComputeISPCObjectSuffixes(target);
2653
0
  bool const extra_objects = (ispcArchSuffixes.size() > 1);
2654
2655
0
  std::vector<std::string> configsList =
2656
0
    this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
2657
0
  for (std::string const& config : configsList) {
2658
2659
0
    std::string rootObjectDir = target->GetObjectDirectory(config);
2660
0
    std::string headerDir = rootObjectDir;
2661
0
    if (cmValue prop = target->GetProperty("ISPC_HEADER_DIRECTORY")) {
2662
0
      headerDir = cmSystemTools::CollapseFullPath(
2663
0
        cmStrCat(this->GetBinaryDirectory(), '/', *prop));
2664
0
    }
2665
2666
0
    std::vector<cmSourceFile*> sources;
2667
0
    target->GetSourceFiles(sources, config);
2668
2669
    // build up the list of ispc headers and extra objects that this target is
2670
    // generating
2671
0
    for (cmSourceFile const* sf : sources) {
2672
      // Generate this object file's rule file.
2673
0
      std::string const& lang = sf->GetLanguage();
2674
0
      if (lang == "ISPC") {
2675
0
        std::string const& objectName = target->GetObjectName(sf);
2676
2677
        // Drop both ".obj" and the source file extension
2678
0
        std::string ispcSource =
2679
0
          cmSystemTools::GetFilenameWithoutLastExtension(objectName);
2680
0
        ispcSource =
2681
0
          cmSystemTools::GetFilenameWithoutLastExtension(ispcSource);
2682
2683
0
        auto headerPath =
2684
0
          cmStrCat(headerDir, '/', ispcSource, *ispcHeaderSuffixProp);
2685
0
        target->AddISPCGeneratedHeader(headerPath, config);
2686
0
        if (extra_objects) {
2687
0
          std::vector<std::string> objs = detail::ComputeISPCExtraObjects(
2688
0
            objectName, rootObjectDir, ispcArchSuffixes);
2689
0
          target->AddISPCGeneratedObject(std::move(objs), config);
2690
0
        }
2691
0
      }
2692
0
    }
2693
0
  }
2694
0
}
2695
2696
void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target)
2697
0
{
2698
0
  std::vector<std::string> configsList =
2699
0
    this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
2700
2701
0
  for (std::string const& config : configsList) {
2702
    // FIXME: Refactor collection of sources to not evaluate object
2703
    // libraries.
2704
0
    std::vector<cmSourceFile*> sources;
2705
0
    target->GetSourceFiles(sources, config);
2706
2707
0
    std::string const configUpper = cmSystemTools::UpperCase(config);
2708
0
    static std::array<std::string, 4> const langs = { { "C", "CXX", "OBJC",
2709
0
                                                        "OBJCXX" } };
2710
2711
0
    std::set<std::string> pchLangSet;
2712
0
    if (this->GetGlobalGenerator()->IsXcode()) {
2713
0
      for (std::string const& lang : langs) {
2714
0
        std::string const pchHeader = target->GetPchHeader(config, lang, "");
2715
0
        if (!pchHeader.empty()) {
2716
0
          pchLangSet.emplace(lang);
2717
0
        }
2718
0
      }
2719
0
    }
2720
2721
0
    for (std::string const& lang : langs) {
2722
0
      auto langSources = std::count_if(
2723
0
        sources.begin(), sources.end(), [lang](cmSourceFile* sf) {
2724
0
          return lang == sf->GetLanguage() &&
2725
0
            !sf->GetProperty("SKIP_PRECOMPILE_HEADERS");
2726
0
        });
2727
0
      if (langSources == 0) {
2728
0
        continue;
2729
0
      }
2730
2731
0
      std::vector<std::string> pchArchs = target->GetPchArchs(config, lang);
2732
0
      if (pchArchs.size() > 1) {
2733
0
        std::string useMultiArchPch;
2734
0
        for (std::string const& arch : pchArchs) {
2735
0
          std::string const pchHeader =
2736
0
            target->GetPchHeader(config, lang, arch);
2737
0
          if (!pchHeader.empty()) {
2738
0
            useMultiArchPch = cmStrCat(useMultiArchPch, ";-Xarch_", arch,
2739
0
                                       ";-include", pchHeader);
2740
0
          }
2741
0
        }
2742
2743
0
        if (!useMultiArchPch.empty()) {
2744
2745
0
          target->Target->AppendProperty(
2746
0
            cmStrCat(lang, "_COMPILE_OPTIONS_USE_PCH"),
2747
0
            cmStrCat("$<$<CONFIG:", config, ">:", useMultiArchPch, '>'));
2748
0
        }
2749
0
      }
2750
2751
0
      for (std::string const& arch : pchArchs) {
2752
0
        std::string const pchSource = target->GetPchSource(config, lang, arch);
2753
0
        std::string const pchHeader = target->GetPchHeader(config, lang, arch);
2754
2755
0
        if (pchSource.empty() || pchHeader.empty()) {
2756
0
          if (this->GetGlobalGenerator()->IsXcode() && !pchLangSet.empty()) {
2757
0
            for (auto* sf : sources) {
2758
0
              auto const sourceLanguage = sf->GetLanguage();
2759
0
              if (!sourceLanguage.empty() &&
2760
0
                  pchLangSet.find(sourceLanguage) == pchLangSet.end()) {
2761
0
                sf->SetProperty("SKIP_PRECOMPILE_HEADERS", "ON");
2762
0
              }
2763
0
            }
2764
0
          }
2765
0
          continue;
2766
0
        }
2767
2768
0
        cmValue pchExtension =
2769
0
          this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
2770
2771
0
        if (pchExtension.IsEmpty()) {
2772
0
          continue;
2773
0
        }
2774
2775
0
        auto* reuseTarget = target->GetPchReuseTarget();
2776
0
        bool const haveReuseTarget = reuseTarget && reuseTarget != target;
2777
2778
0
        auto* pch_sf = this->Makefile->GetOrCreateSource(
2779
0
          pchSource, false, cmSourceFileLocationKind::Known);
2780
0
        pch_sf->SetSpecialSourceType(
2781
0
          cmSourceFile::SpecialSourceType::PchSource);
2782
        // PCH sources should never be scanned as they cannot contain C++
2783
        // module references.
2784
0
        pch_sf->SetProperty("CXX_SCAN_FOR_MODULES", "0");
2785
2786
0
        if (!this->GetGlobalGenerator()->IsXcode()) {
2787
0
          if (!haveReuseTarget) {
2788
0
            target->AddSource(pchSource, true);
2789
0
          }
2790
2791
0
          std::string const pchFile = target->GetPchFile(config, lang, arch);
2792
2793
          // Exclude the pch files from linking
2794
0
          if (this->Makefile->IsOn("CMAKE_LINK_PCH")) {
2795
0
            if (!haveReuseTarget) {
2796
0
              pch_sf->AppendProperty(
2797
0
                "OBJECT_OUTPUTS",
2798
0
                cmStrCat("$<$<CONFIG:", config, ">:", pchFile, '>'));
2799
0
            } else {
2800
0
              if (this->Makefile->IsOn("CMAKE_PCH_COPY_COMPILE_PDB")) {
2801
2802
0
                std::string const compilerId =
2803
0
                  this->Makefile->GetSafeDefinition(
2804
0
                    cmStrCat("CMAKE_", lang, "_COMPILER_ID"));
2805
2806
0
                std::string const compilerVersion =
2807
0
                  this->Makefile->GetSafeDefinition(
2808
0
                    cmStrCat("CMAKE_", lang, "_COMPILER_VERSION"));
2809
2810
0
                std::string const langFlags =
2811
0
                  this->Makefile->GetSafeDefinition(
2812
0
                    cmStrCat("CMAKE_", lang, "_FLAGS_", configUpper));
2813
2814
0
                bool editAndContinueDebugInfo = false;
2815
0
                bool programDatabaseDebugInfo = false;
2816
0
                cm::optional<std::string> msvcDebugInformationFormat =
2817
0
                  this->GetMSVCDebugFormatName(config, target);
2818
0
                if (msvcDebugInformationFormat &&
2819
0
                    !msvcDebugInformationFormat->empty()) {
2820
0
                  editAndContinueDebugInfo =
2821
0
                    *msvcDebugInformationFormat == "EditAndContinue";
2822
0
                  programDatabaseDebugInfo =
2823
0
                    *msvcDebugInformationFormat == "ProgramDatabase";
2824
0
                } else {
2825
0
                  editAndContinueDebugInfo =
2826
0
                    langFlags.find("/ZI") != std::string::npos ||
2827
0
                    langFlags.find("-ZI") != std::string::npos;
2828
0
                  programDatabaseDebugInfo =
2829
0
                    langFlags.find("/Zi") != std::string::npos ||
2830
0
                    langFlags.find("-Zi") != std::string::npos;
2831
0
                }
2832
2833
0
                if (editAndContinueDebugInfo) {
2834
0
                  this->CopyPchCompilePdb(config, lang, target, reuseTarget,
2835
0
                                          { ".pdb", ".idb" });
2836
0
                } else if (programDatabaseDebugInfo) {
2837
0
                  this->CopyPchCompilePdb(config, lang, target, reuseTarget,
2838
0
                                          { ".pdb" });
2839
0
                }
2840
0
              }
2841
2842
              // Link to the pch object file
2843
0
              std::string pchSourceObj;
2844
              // Fastbuild will propagate pch.obj for us, no need to link to it
2845
              // explicitly.
2846
0
              if (!this->GetGlobalGenerator()->IsFastbuild()) {
2847
0
                pchSourceObj =
2848
0
                  reuseTarget->GetPchFileObject(config, lang, arch);
2849
0
              }
2850
2851
0
              if (target->GetType() != cmStateEnums::OBJECT_LIBRARY) {
2852
0
                std::string linkerProperty = "LINK_FLAGS_";
2853
0
                if (target->GetType() == cmStateEnums::STATIC_LIBRARY) {
2854
0
                  linkerProperty = "STATIC_LIBRARY_FLAGS_";
2855
0
                }
2856
0
                target->Target->AppendProperty(
2857
0
                  cmStrCat(linkerProperty, configUpper),
2858
0
                  cmStrCat(' ',
2859
0
                           this->ConvertToOutputFormat(pchSourceObj, SHELL)),
2860
0
                  cm::nullopt, true);
2861
0
              } else if (reuseTarget->GetType() ==
2862
0
                         cmStateEnums::OBJECT_LIBRARY) {
2863
0
                target->Target->AppendProperty(
2864
0
                  "INTERFACE_LINK_LIBRARIES",
2865
0
                  cmStrCat("$<$<CONFIG:", config,
2866
0
                           ">:$<LINK_ONLY:", pchSourceObj, ">>"));
2867
0
              }
2868
0
            }
2869
0
          } else {
2870
0
            pch_sf->SetProperty("PCH_EXTENSION", pchExtension);
2871
0
          }
2872
2873
          // Add pchHeader to source files, which will
2874
          // be grouped as "Precompile Header File"
2875
0
          auto* pchHeader_sf = this->Makefile->GetOrCreateSource(
2876
0
            pchHeader, false, cmSourceFileLocationKind::Known);
2877
0
          pchHeader_sf->SetSpecialSourceType(
2878
0
            cmSourceFile::SpecialSourceType::PchHeader);
2879
0
          std::string err;
2880
0
          pchHeader_sf->ResolveFullPath(&err);
2881
0
          if (!err.empty()) {
2882
0
            std::ostringstream msg;
2883
0
            msg << "Unable to resolve full path of PCH-header '" << pchHeader
2884
0
                << "' assigned to target " << target->GetName()
2885
0
                << ", although its path is supposed to be known!";
2886
0
            this->IssueMessage(MessageType::FATAL_ERROR, msg.str());
2887
0
          }
2888
0
          target->AddSource(pchHeader);
2889
0
        }
2890
0
      }
2891
0
    }
2892
0
  }
2893
0
}
2894
2895
void cmLocalGenerator::CopyPchCompilePdb(
2896
  std::string const& config, std::string const& language,
2897
  cmGeneratorTarget* target, cmGeneratorTarget* reuseTarget,
2898
  std::vector<std::string> const& extensions)
2899
0
{
2900
0
  std::string const copy_script = cmStrCat(target->GetSupportDirectory(),
2901
0
                                           "/copy_idb_pdb_", config, ".cmake");
2902
0
  cmGeneratedFileStream file(copy_script);
2903
2904
0
  file << "# CMake generated file\n";
2905
2906
0
  file << "# The compiler generated pdb file needs to be written to disk\n"
2907
0
       << "# by mspdbsrv. The foreach retry loop is needed to make sure\n"
2908
0
       << "# the pdb file is ready to be copied.\n\n";
2909
2910
0
  auto configGenex = [&](cm::string_view expr) -> std::string {
2911
0
    if (this->GetGlobalGenerator()->IsMultiConfig()) {
2912
0
      return cmStrCat("$<$<CONFIG:", config, ">:", expr, '>');
2913
0
    }
2914
0
    return std::string(expr);
2915
0
  };
2916
2917
0
  std::vector<std::string> outputs;
2918
0
  auto replaceExtension = [](std::string const& path,
2919
0
                             std::string const& ext) -> std::string {
2920
0
    auto const dir = cmSystemTools::GetFilenamePath(path);
2921
0
    auto const base = cmSystemTools::GetFilenameWithoutLastExtension(path);
2922
0
    if (dir.empty()) {
2923
0
      return cmStrCat(base, ext);
2924
0
    }
2925
0
    return cmStrCat(dir, '/', base, ext);
2926
0
  };
2927
0
  for (auto const& extension : extensions) {
2928
0
    std::string const from_file =
2929
0
      replaceExtension(reuseTarget->GetCompilePDBPath(config), extension);
2930
0
    std::string const to_dir = target->GetCompilePDBDirectory(config);
2931
0
    std::string const to_file =
2932
0
      replaceExtension(reuseTarget->GetCompilePDBName(config), extension);
2933
0
    std::string const dest_file = cmStrCat(to_dir, '/', to_file);
2934
2935
0
    file << "foreach(retry RANGE 1 30)\n";
2936
0
    file << "  if (EXISTS \"" << from_file << "\" AND (NOT EXISTS \""
2937
0
         << dest_file << "\" OR NOT \"" << dest_file << "\" IS_NEWER_THAN \""
2938
0
         << from_file << "\"))\n";
2939
0
    file << "    file(MAKE_DIRECTORY \"" << to_dir << "\")\n";
2940
0
    file << "    execute_process(COMMAND ${CMAKE_COMMAND} -E copy";
2941
0
    file << " \"" << from_file << "\"" << " \"" << to_dir
2942
0
         << "\" RESULT_VARIABLE result " << " ERROR_QUIET)\n";
2943
0
    file << "    if (NOT result EQUAL 0)\n"
2944
0
         << "      execute_process(COMMAND ${CMAKE_COMMAND}"
2945
0
         << " -E sleep 1)\n"
2946
0
         << "    else()\n";
2947
0
    file << "      break()\n"
2948
0
         << "    endif()\n";
2949
0
    file << "  elseif(NOT EXISTS \"" << from_file << "\")\n"
2950
0
         << "    execute_process(COMMAND ${CMAKE_COMMAND}" << " -E sleep 1)\n"
2951
0
         << "  endif()\n";
2952
0
    file << "endforeach()\n";
2953
0
    outputs.push_back(configGenex(dest_file));
2954
0
  }
2955
2956
0
  cmCustomCommandLines commandLines =
2957
0
    cmMakeSingleCommandLine({ configGenex(cmSystemTools::GetCMakeCommand()),
2958
0
                              configGenex("-P"), configGenex(copy_script) });
2959
2960
0
  auto const comment =
2961
0
    cmStrCat("Copying PDB for PCH reuse from ", reuseTarget->GetName(),
2962
0
             " for ", target->GetName());
2963
0
  ;
2964
2965
0
  auto cc = cm::make_unique<cmCustomCommand>();
2966
0
  cc->SetCommandLines(commandLines);
2967
0
  cc->SetComment(comment.c_str());
2968
0
  cc->SetStdPipesUTF8(true);
2969
0
  cc->AppendDepends(
2970
0
    { reuseTarget->GetPchFile(config, language), copy_script });
2971
  // Fastbuild needs to know that this custom command actually depends on a
2972
  // target that produces PCH, so it can sort by dependencies correctly.
2973
0
  if (this->GetGlobalGenerator()->IsFastbuild()) {
2974
0
    cc->AppendDepends({ reuseTarget->GetName() });
2975
0
  }
2976
2977
0
  if (this->GetGlobalGenerator()->IsVisualStudio()) {
2978
0
    cc->SetByproducts(outputs);
2979
0
    this->AddCustomCommandToTarget(
2980
0
      target->GetName(), cmCustomCommandType::PRE_BUILD, std::move(cc),
2981
0
      cmObjectLibraryCommands::Accept);
2982
0
  } else {
2983
0
    cc->SetOutputs(outputs);
2984
0
    cmSourceFile* copy_rule = this->AddCustomCommandToOutput(std::move(cc));
2985
0
    if (copy_rule) {
2986
0
      copy_rule->SetProperty("CXX_SCAN_FOR_MODULES", "0");
2987
0
      auto* pch_pdb_sf = target->AddSource(copy_rule->ResolveFullPath());
2988
0
      pch_pdb_sf->SetSpecialSourceType(
2989
0
        cmSourceFile::SpecialSourceType::PchPdbReuseSource);
2990
0
    }
2991
0
  }
2992
0
}
2993
2994
cm::optional<std::string> cmLocalGenerator::GetMSVCDebugFormatName(
2995
  std::string const& config, cmGeneratorTarget const* target)
2996
0
{
2997
  // MSVC debug information format selection is activated by the presence
2998
  // of a default whether or not it is overridden by a property.
2999
0
  cm::optional<std::string> msvcDebugInformationFormat;
3000
0
  cmValue msvcDebugInformationFormatDefault = this->Makefile->GetDefinition(
3001
0
    "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT");
3002
0
  if (cmNonempty(msvcDebugInformationFormatDefault)) {
3003
0
    cmValue msvcDebugInformationFormatValue =
3004
0
      target->GetProperty("MSVC_DEBUG_INFORMATION_FORMAT");
3005
0
    if (!msvcDebugInformationFormatValue) {
3006
0
      msvcDebugInformationFormatValue = msvcDebugInformationFormatDefault;
3007
0
    }
3008
0
    msvcDebugInformationFormat = cmGeneratorExpression::Evaluate(
3009
0
      *msvcDebugInformationFormatValue, this, config, target);
3010
0
  }
3011
0
  return msvcDebugInformationFormat;
3012
0
}
3013
3014
cm::optional<cmSwiftCompileMode> cmLocalGenerator::GetSwiftCompileMode(
3015
  cmGeneratorTarget const* target, std::string const& config)
3016
0
{
3017
0
  cmMakefile const* mf = this->GetMakefile();
3018
0
  cmValue const swiftCompileModeDefault =
3019
0
    mf->GetDefinition("CMAKE_Swift_COMPILATION_MODE_DEFAULT");
3020
0
  if (!cmNonempty(swiftCompileModeDefault)) {
3021
0
    return {};
3022
0
  }
3023
0
  cmValue swiftCompileMode = target->GetProperty("Swift_COMPILATION_MODE");
3024
0
  if (!swiftCompileMode) {
3025
0
    swiftCompileMode = swiftCompileModeDefault;
3026
0
  }
3027
3028
0
  std::string const expandedCompileMode =
3029
0
    cmGeneratorExpression::Evaluate(*swiftCompileMode, this, config, target);
3030
0
  if (expandedCompileMode == "wholemodule") {
3031
0
    return cmSwiftCompileMode::Wholemodule;
3032
0
  }
3033
0
  if (expandedCompileMode == "singlefile") {
3034
0
    return cmSwiftCompileMode::Singlefile;
3035
0
  }
3036
0
  if (expandedCompileMode == "incremental") {
3037
0
    return cmSwiftCompileMode::Incremental;
3038
0
  }
3039
0
  return cmSwiftCompileMode::Unknown;
3040
0
}
3041
3042
bool cmLocalGenerator::IsSplitSwiftBuild() const
3043
0
{
3044
0
  return cmNonempty(this->GetMakefile()->GetDefinition(
3045
0
    "CMAKE_Swift_COMPILATION_MODE_DEFAULT"));
3046
0
}
3047
3048
namespace {
3049
3050
inline void RegisterUnitySources(cmGeneratorTarget* target, cmSourceFile* sf,
3051
                                 std::string const& filename)
3052
0
{
3053
0
  target->AddSourceFileToUnityBatch(sf->ResolveFullPath());
3054
0
  sf->SetProperty("UNITY_SOURCE_FILE", filename);
3055
0
}
3056
}
3057
3058
cmLocalGenerator::UnitySource cmLocalGenerator::WriteUnitySource(
3059
  cmGeneratorTarget* target, std::vector<std::string> const& configs,
3060
  cmRange<std::vector<UnityBatchedSource>::const_iterator> sources,
3061
  cmValue beforeInclude, cmValue afterInclude, std::string filename,
3062
  std::string const& unityFileDirectory, UnityPathMode pathMode) const
3063
0
{
3064
0
  cmValue uniqueIdName = target->GetProperty("UNITY_BUILD_UNIQUE_ID");
3065
0
  cmGeneratedFileStream file(
3066
0
    filename, false, target->GetGlobalGenerator()->GetMakefileEncoding());
3067
0
  file.SetCopyIfDifferent(true);
3068
0
  file << "/* generated by CMake */\n\n";
3069
3070
0
  bool perConfig = false;
3071
0
  for (UnityBatchedSource const& ubs : sources) {
3072
0
    cm::optional<std::string> cond;
3073
0
    if (ubs.Configs.size() != configs.size()) {
3074
0
      perConfig = true;
3075
0
      cond = std::string();
3076
0
      cm::string_view sep;
3077
0
      for (size_t ci : ubs.Configs) {
3078
0
        cond = cmStrCat(*cond, sep, "defined(CMAKE_UNITY_CONFIG_",
3079
0
                        cmSystemTools::UpperCase(configs[ci]), ')');
3080
0
        sep = " || "_s;
3081
0
      }
3082
0
    }
3083
0
    RegisterUnitySources(target, ubs.Source, filename);
3084
0
    WriteUnitySourceInclude(file, cond, ubs.Source->ResolveFullPath(),
3085
0
                            beforeInclude, afterInclude, uniqueIdName,
3086
0
                            pathMode, unityFileDirectory);
3087
0
  }
3088
3089
0
  return UnitySource(std::move(filename), perConfig);
3090
0
}
3091
3092
void cmLocalGenerator::WriteUnitySourceInclude(
3093
  std::ostream& unity_file, cm::optional<std::string> const& cond,
3094
  std::string const& sf_full_path, cmValue beforeInclude, cmValue afterInclude,
3095
  cmValue uniqueIdName, UnityPathMode pathMode,
3096
  std::string const& unityFileDirectory) const
3097
0
{
3098
0
  if (cond) {
3099
0
    unity_file << "#if " << *cond << "\n";
3100
0
  }
3101
3102
0
  std::string pathToHash;
3103
0
  std::string relocatableIncludePath;
3104
0
  auto PathEqOrSubDir = [](std::string const& a, std::string const& b) {
3105
0
    return (cmSystemTools::ComparePath(a, b) ||
3106
0
            cmSystemTools::IsSubDirectory(a, b));
3107
0
  };
3108
0
  auto const path = cmSystemTools::GetFilenamePath(sf_full_path);
3109
0
  if (PathEqOrSubDir(path, this->GetBinaryDirectory())) {
3110
0
    relocatableIncludePath =
3111
0
      cmSystemTools::RelativePath(unityFileDirectory, sf_full_path);
3112
0
    pathToHash = "BLD_" +
3113
0
      cmSystemTools::RelativePath(this->GetBinaryDirectory(), sf_full_path);
3114
0
  } else if (PathEqOrSubDir(path, this->GetSourceDirectory())) {
3115
0
    relocatableIncludePath =
3116
0
      cmSystemTools::RelativePath(this->GetSourceDirectory(), sf_full_path);
3117
0
    pathToHash = "SRC_" + relocatableIncludePath;
3118
0
  } else {
3119
0
    relocatableIncludePath = sf_full_path;
3120
0
    pathToHash = "ABS_" + sf_full_path;
3121
0
  }
3122
3123
0
  if (cmNonempty(uniqueIdName)) {
3124
0
    cmCryptoHash hasher(cmCryptoHash::AlgoMD5);
3125
0
    unity_file << "/* " << pathToHash << " */\n"
3126
0
               << "#undef " << *uniqueIdName << "\n"
3127
0
               << "#define " << *uniqueIdName << " unity_"
3128
0
               << hasher.HashString(pathToHash) << "\n";
3129
0
  }
3130
3131
0
  if (beforeInclude) {
3132
0
    unity_file << *beforeInclude << "\n";
3133
0
  }
3134
3135
  // clang-tidy-17 has new include checks that needs NOLINT too.
3136
0
  unity_file
3137
0
    << "/* NOLINTNEXTLINE(bugprone-suspicious-include,misc-include-cleaner) "
3138
0
       "*/\n";
3139
0
  if (pathMode == UnityPathMode::Relative) {
3140
0
    unity_file << "#include \"" << relocatableIncludePath << "\"\n";
3141
0
  } else {
3142
0
    unity_file << "#include \"" << sf_full_path << "\"\n";
3143
0
  }
3144
3145
0
  if (afterInclude) {
3146
0
    unity_file << *afterInclude << "\n";
3147
0
  }
3148
0
  if (cond) {
3149
0
    unity_file << "#endif\n";
3150
0
  }
3151
0
  unity_file << "\n";
3152
0
}
3153
3154
namespace {
3155
std::string unity_file_extension(std::string const& lang)
3156
0
{
3157
0
  std::string extension;
3158
0
  if (lang == "C") {
3159
0
    extension = "_c.c";
3160
0
  } else if (lang == "CXX") {
3161
0
    extension = "_cxx.cxx";
3162
0
  } else if (lang == "CUDA") {
3163
0
    extension = "_cu.cu";
3164
0
  } else if (lang == "OBJC") {
3165
0
    extension = "_m.m";
3166
0
  } else if (lang == "OBJCXX") {
3167
0
    extension = "_mm.mm";
3168
0
  }
3169
0
  return extension;
3170
0
}
3171
3172
char const* unity_file_prefix(cmGeneratorTarget* target)
3173
0
{
3174
0
  if (cmValue val = target->GetProperty("UNITY_BUILD_FILENAME_PREFIX")) {
3175
0
    return val->c_str();
3176
0
  }
3177
0
  return "unity_";
3178
0
}
3179
}
3180
3181
std::vector<cmLocalGenerator::UnitySource>
3182
cmLocalGenerator::AddUnityFilesModeAuto(
3183
  cmGeneratorTarget* target, std::string const& lang,
3184
  std::vector<std::string> const& configs,
3185
  std::vector<UnityBatchedSource> const& filtered_sources,
3186
  cmValue beforeInclude, cmValue afterInclude,
3187
  std::string const& filename_base, UnityPathMode pathMode, size_t batchSize)
3188
0
{
3189
0
  if (batchSize == 0) {
3190
0
    batchSize = filtered_sources.size();
3191
0
  }
3192
0
  char const* filename_prefix = unity_file_prefix(target);
3193
0
  std::vector<UnitySource> unity_files;
3194
0
  for (size_t itemsLeft = filtered_sources.size(), chunk, batch = 0;
3195
0
       itemsLeft > 0; itemsLeft -= chunk, ++batch) {
3196
3197
0
    chunk = std::min(itemsLeft, batchSize);
3198
3199
0
    std::string filename = cmStrCat(filename_base, filename_prefix, batch,
3200
0
                                    unity_file_extension(lang));
3201
0
    auto const begin = filtered_sources.begin() + batch * batchSize;
3202
0
    auto const end = begin + chunk;
3203
0
    unity_files.emplace_back(this->WriteUnitySource(
3204
0
      target, configs, cmMakeRange(begin, end), beforeInclude, afterInclude,
3205
0
      std::move(filename), filename_base, pathMode));
3206
0
  }
3207
0
  return unity_files;
3208
0
}
3209
3210
std::vector<cmLocalGenerator::UnitySource>
3211
cmLocalGenerator::AddUnityFilesModeGroup(
3212
  cmGeneratorTarget* target, std::string const& lang,
3213
  std::vector<std::string> const& configs,
3214
  std::vector<UnityBatchedSource> const& filtered_sources,
3215
  cmValue beforeInclude, cmValue afterInclude,
3216
  std::string const& filename_base, UnityPathMode pathMode)
3217
0
{
3218
0
  std::vector<UnitySource> unity_files;
3219
3220
  // sources organized by group name. Drop any source
3221
  // without a group
3222
0
  std::unordered_map<std::string, std::vector<UnityBatchedSource>>
3223
0
    explicit_mapping;
3224
0
  for (UnityBatchedSource const& ubs : filtered_sources) {
3225
0
    if (cmValue value = ubs.Source->GetProperty("UNITY_GROUP")) {
3226
0
      auto i = explicit_mapping.find(*value);
3227
0
      if (i == explicit_mapping.end()) {
3228
0
        std::vector<UnityBatchedSource> sources{ ubs };
3229
0
        explicit_mapping.emplace(*value, std::move(sources));
3230
0
      } else {
3231
0
        i->second.emplace_back(ubs);
3232
0
      }
3233
0
    }
3234
0
  }
3235
3236
0
  char const* filename_prefix = unity_file_prefix(target);
3237
0
  for (auto const& item : explicit_mapping) {
3238
0
    auto const& name = item.first;
3239
0
    std::string filename = cmStrCat(filename_base, filename_prefix, name,
3240
0
                                    unity_file_extension(lang));
3241
0
    unity_files.emplace_back(this->WriteUnitySource(
3242
0
      target, configs, cmMakeRange(item.second), beforeInclude, afterInclude,
3243
0
      std::move(filename), filename_base, pathMode));
3244
0
  }
3245
3246
0
  return unity_files;
3247
0
}
3248
3249
void cmLocalGenerator::AddUnityBuild(cmGeneratorTarget* target)
3250
0
{
3251
  // cmFastbuildNormalTargetGenerator handles unity build.
3252
0
  if (this->GetGlobalGenerator()->IsFastbuild() ||
3253
0
      !target->GetPropertyAsBool("UNITY_BUILD")) {
3254
0
    return;
3255
0
  }
3256
3257
0
  std::vector<UnityBatchedSource> unitySources;
3258
3259
0
  std::vector<std::string> configs =
3260
0
    this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
3261
3262
0
  std::map<cmSourceFile const*, size_t> index;
3263
3264
0
  for (size_t ci = 0; ci < configs.size(); ++ci) {
3265
    // FIXME: Refactor collection of sources to not evaluate object libraries.
3266
    // Their final set of object files might be transformed by unity builds.
3267
0
    std::vector<cmSourceFile*> sources;
3268
0
    target->GetSourceFiles(sources, configs[ci]);
3269
0
    for (cmSourceFile* sf : sources) {
3270
      // Files which need C++ scanning cannot participate in unity builds as
3271
      // there is a single place in TUs that may perform module-dependency bits
3272
      // and a unity source cannot `#include` them in-order and represent a
3273
      // valid TU.
3274
0
      if (sf->GetLanguage() == "CXX"_s &&
3275
0
          target->NeedDyndepForSource("CXX", configs[ci], sf)) {
3276
0
        continue;
3277
0
      }
3278
3279
0
      auto mi = index.find(sf);
3280
0
      if (mi == index.end()) {
3281
0
        unitySources.emplace_back(sf);
3282
0
        std::map<cmSourceFile const*, size_t>::value_type entry(
3283
0
          sf, unitySources.size() - 1);
3284
0
        mi = index.insert(entry).first;
3285
0
      }
3286
0
      unitySources[mi->second].Configs.emplace_back(ci);
3287
0
    }
3288
0
  }
3289
3290
0
  std::string filename_base =
3291
0
    cmStrCat(target->GetCMFSupportDirectory(), "/Unity/");
3292
3293
0
  cmValue batchSizeString = target->GetProperty("UNITY_BUILD_BATCH_SIZE");
3294
0
  size_t const unityBatchSize = batchSizeString
3295
0
    ? static_cast<size_t>(std::atoi(batchSizeString->c_str()))
3296
0
    : 0;
3297
3298
0
  cmValue beforeInclude =
3299
0
    target->GetProperty("UNITY_BUILD_CODE_BEFORE_INCLUDE");
3300
0
  cmValue afterInclude = target->GetProperty("UNITY_BUILD_CODE_AFTER_INCLUDE");
3301
0
  cmValue unityMode = target->GetProperty("UNITY_BUILD_MODE");
3302
0
  UnityPathMode pathMode = target->GetPropertyAsBool("UNITY_BUILD_RELOCATABLE")
3303
0
    ? UnityPathMode::Relative
3304
0
    : UnityPathMode::Absolute;
3305
3306
0
  for (std::string lang : { "C", "CXX", "OBJC", "OBJCXX", "CUDA" }) {
3307
0
    std::vector<UnityBatchedSource> filtered_sources;
3308
0
    std::copy_if(unitySources.begin(), unitySources.end(),
3309
0
                 std::back_inserter(filtered_sources),
3310
0
                 [&](UnityBatchedSource const& ubs) -> bool {
3311
0
                   cmSourceFile* sf = ubs.Source;
3312
0
                   return sf->GetLanguage() == lang &&
3313
0
                     !sf->GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION") &&
3314
0
                     !sf->GetPropertyAsBool("HEADER_FILE_ONLY") &&
3315
0
                     !sf->GetProperty("COMPILE_OPTIONS") &&
3316
0
                     !sf->GetProperty("COMPILE_DEFINITIONS") &&
3317
0
                     !sf->GetProperty("COMPILE_FLAGS") &&
3318
0
                     !sf->GetProperty("INCLUDE_DIRECTORIES");
3319
0
                 });
3320
3321
0
    std::vector<UnitySource> unity_files;
3322
0
    if (!unityMode || *unityMode == "BATCH") {
3323
0
      unity_files = AddUnityFilesModeAuto(
3324
0
        target, lang, configs, filtered_sources, beforeInclude, afterInclude,
3325
0
        filename_base, pathMode, unityBatchSize);
3326
0
    } else if (unityMode && *unityMode == "GROUP") {
3327
0
      unity_files = AddUnityFilesModeGroup(
3328
0
        target, lang, configs, filtered_sources, beforeInclude, afterInclude,
3329
0
        filename_base, pathMode);
3330
0
    } else {
3331
      // unity mode is set to an unsupported value
3332
0
      std::string e("Invalid UNITY_BUILD_MODE value of " + *unityMode +
3333
0
                    " assigned to target " + target->GetName() +
3334
0
                    ". Acceptable values are BATCH and GROUP.");
3335
0
      this->IssueMessage(MessageType::FATAL_ERROR, e);
3336
0
    }
3337
3338
0
    for (UnitySource const& file : unity_files) {
3339
0
      auto* unity = this->GetMakefile()->GetOrCreateSource(file.Path);
3340
0
      unity->SetSpecialSourceType(
3341
0
        cmSourceFile::SpecialSourceType::UnitySource);
3342
0
      target->AddSource(file.Path, true);
3343
0
      unity->SetProperty("SKIP_UNITY_BUILD_INCLUSION", "ON");
3344
0
      unity->SetProperty("UNITY_SOURCE_FILE", file.Path);
3345
0
      unity->SetProperty("CXX_SCAN_FOR_MODULES", "0");
3346
0
      if (file.PerConfig) {
3347
0
        unity->SetProperty("COMPILE_DEFINITIONS",
3348
0
                           "CMAKE_UNITY_CONFIG_$<UPPER_CASE:$<CONFIG>>");
3349
0
      }
3350
3351
0
      if (pathMode == UnityPathMode::Relative) {
3352
0
        unity->AppendProperty("INCLUDE_DIRECTORIES",
3353
0
                              this->GetSourceDirectory(), false);
3354
0
      }
3355
0
    }
3356
0
  }
3357
0
}
3358
3359
void cmLocalGenerator::AddPerLanguageLinkFlags(std::string& flags,
3360
                                               cmGeneratorTarget const* target,
3361
                                               std::string const& lang,
3362
                                               std::string const& config)
3363
0
{
3364
0
  switch (target->GetType()) {
3365
0
    case cmStateEnums::MODULE_LIBRARY:
3366
0
    case cmStateEnums::SHARED_LIBRARY:
3367
0
    case cmStateEnums::EXECUTABLE:
3368
0
      break;
3369
0
    default:
3370
0
      return;
3371
0
  }
3372
3373
0
  std::string langLinkFlags =
3374
0
    this->Makefile->GetSafeDefinition(cmStrCat("CMAKE_", lang, "_LINK_FLAGS"));
3375
3376
0
  switch (target->GetPolicyStatusCMP0210()) {
3377
0
    case cmPolicies::WARN:
3378
      // WARN only when CMAKE_<LANG>_LINK_FLAGS is set, and when the current
3379
      // target is not an executable, and CMAKE_<LANG>_LINK_FLAGS is not equal
3380
      // to CMAKE_EXECUTABLE_CREATE_<LANG>_FLAGS. This warns users trying to
3381
      // use the NEW behavior on old projects (since CMake will be ignoring
3382
      // their wishes), while also exempting cases when the latter variable
3383
      // (substituted for the former spelling under the NEW behavior) is being
3384
      // used legitimately by CMake.
3385
0
      if (!langLinkFlags.empty() &&
3386
0
          target->GetType() != cmStateEnums::EXECUTABLE &&
3387
0
          langLinkFlags !=
3388
0
            this->Makefile->GetSafeDefinition(
3389
0
              cmStrCat("CMAKE_EXECUTABLE_CREATE_", lang, "_FLAGS"))) {
3390
0
        this->IssueMessage(
3391
0
          MessageType::AUTHOR_WARNING,
3392
0
          cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0210), "\n",
3393
0
                   "For compatibility with older versions of CMake, ",
3394
0
                   "CMAKE_", lang, "_LINK_FLAGS will be ignored for target '",
3395
0
                   target->GetName(), "'."));
3396
0
      }
3397
0
      CM_FALLTHROUGH;
3398
0
    case cmPolicies::OLD:
3399
      // OLD behavior is to do nothing here, since the use of
3400
      // CMAKE_<LANG>_LINK_FLAGS for EXECUTABLEs is handled elsewhere.
3401
0
      break;
3402
0
    case cmPolicies::NEW:
3403
      // NEW behavior is to support per-language link flags for all target
3404
      // types.
3405
0
      this->AppendLinkFlagsWithParsing(flags, langLinkFlags, target, lang);
3406
0
      if (!config.empty()) {
3407
0
        std::string lankLinkFlagsConfig =
3408
0
          this->Makefile->GetSafeDefinition(cmStrCat(
3409
0
            "CMAKE_", lang, "_LINK_FLAGS_", cmSystemTools::UpperCase(config)));
3410
0
        this->AppendLinkFlagsWithParsing(flags, lankLinkFlagsConfig, target,
3411
0
                                         lang);
3412
0
      }
3413
0
      break;
3414
0
  }
3415
0
}
3416
3417
void cmLocalGenerator::AppendTargetCreationLinkFlags(
3418
  std::string& flags, cmGeneratorTarget const* target,
3419
  std::string const& linkLanguage)
3420
0
{
3421
0
  std::string createFlagsVar;
3422
0
  cmValue createFlagsVal;
3423
0
  switch (target->GetType()) {
3424
0
    case cmStateEnums::STATIC_LIBRARY:
3425
0
      break;
3426
0
    case cmStateEnums::MODULE_LIBRARY:
3427
0
      createFlagsVar =
3428
0
        cmStrCat("CMAKE_SHARED_MODULE_CREATE_", linkLanguage, "_FLAGS");
3429
0
      createFlagsVal = this->Makefile->GetDefinition(createFlagsVar);
3430
      // On some platforms we use shared library creation flags for modules.
3431
0
      CM_FALLTHROUGH;
3432
0
    case cmStateEnums::SHARED_LIBRARY:
3433
0
      if (!createFlagsVal) {
3434
0
        createFlagsVar =
3435
0
          cmStrCat("CMAKE_SHARED_LIBRARY_CREATE_", linkLanguage, "_FLAGS");
3436
0
        createFlagsVal = this->Makefile->GetDefinition(createFlagsVar);
3437
0
      }
3438
0
      break;
3439
0
    case cmStateEnums::EXECUTABLE:
3440
0
      createFlagsVar = target->GetPolicyStatusCMP0210() == cmPolicies::NEW
3441
0
        ? cmStrCat("CMAKE_EXECUTABLE_CREATE_", linkLanguage, "_FLAGS")
3442
0
        : cmStrCat("CMAKE_", linkLanguage, "_LINK_FLAGS");
3443
0
      createFlagsVal = this->Makefile->GetDefinition(createFlagsVar);
3444
0
      break;
3445
0
    default:
3446
0
      break;
3447
0
  }
3448
0
  if (createFlagsVal) {
3449
0
    this->AppendFlags(flags, *createFlagsVal, createFlagsVar, target,
3450
0
                      cmBuildStep::Link, linkLanguage);
3451
0
  }
3452
0
}
3453
3454
void cmLocalGenerator::AppendLinkerTypeFlags(std::string& flags,
3455
                                             cmGeneratorTarget* target,
3456
                                             std::string const& config,
3457
                                             std::string const& linkLanguage)
3458
0
{
3459
0
  switch (target->GetType()) {
3460
0
    case cmStateEnums::EXECUTABLE:
3461
0
    case cmStateEnums::SHARED_LIBRARY:
3462
0
    case cmStateEnums::MODULE_LIBRARY:
3463
0
      break;
3464
0
    default:
3465
0
      return;
3466
0
  }
3467
3468
0
  auto linkMode =
3469
0
    cmStrCat("CMAKE_", linkLanguage, target->IsDeviceLink() ? "_DEVICE_" : "_",
3470
0
             "LINK_MODE");
3471
0
  auto mode = this->Makefile->GetDefinition(linkMode);
3472
0
  if (mode && mode != "DRIVER"_s) {
3473
0
    return;
3474
0
  }
3475
3476
0
  auto linkerType = target->GetLinkerTypeProperty(linkLanguage, config);
3477
0
  if (linkerType.empty()) {
3478
0
    linkerType = "DEFAULT";
3479
0
  }
3480
0
  auto usingLinker =
3481
0
    cmStrCat("CMAKE_", linkLanguage, "_USING_",
3482
0
             target->IsDeviceLink() ? "DEVICE_" : "", "LINKER_", linkerType);
3483
0
  auto linkerTypeFlags = this->Makefile->GetDefinition(usingLinker);
3484
0
  if (linkerTypeFlags) {
3485
0
    if (!linkerTypeFlags.IsEmpty()) {
3486
0
      auto linkerFlags = cmExpandListWithBacktrace(linkerTypeFlags);
3487
0
      target->ResolveLinkerWrapper(linkerFlags, linkLanguage);
3488
0
      this->AppendFlags(flags, linkerFlags);
3489
0
    }
3490
0
  } else if (linkerType != "DEFAULT"_s) {
3491
0
    auto isCMakeLinkerType = [](std::string const& type) -> bool {
3492
0
      return std::all_of(type.cbegin(), type.cend(),
3493
0
                         [](char c) { return std::isupper(c); });
3494
0
    };
3495
0
    if (isCMakeLinkerType(linkerType)) {
3496
0
      this->IssueMessage(
3497
0
        MessageType::FATAL_ERROR,
3498
0
        cmStrCat("LINKER_TYPE '", linkerType,
3499
0
                 "' is unknown or not supported by this toolchain."));
3500
0
    } else {
3501
0
      this->IssueMessage(
3502
0
        MessageType::FATAL_ERROR,
3503
0
        cmStrCat("LINKER_TYPE '", linkerType,
3504
0
                 "' is unknown. Did you forget to define the '", usingLinker,
3505
0
                 "' variable?"));
3506
0
    }
3507
0
  }
3508
0
}
3509
3510
void cmLocalGenerator::AddTargetTypeLinkerFlags(
3511
  std::string& flags, cmGeneratorTarget const* target, std::string const& lang,
3512
  std::string const& config)
3513
0
{
3514
0
  std::string linkerFlagsVar;
3515
0
  switch (target->GetType()) {
3516
0
    case cmStateEnums::EXECUTABLE:
3517
0
      linkerFlagsVar = "CMAKE_EXE_LINKER_FLAGS";
3518
0
      break;
3519
0
    case cmStateEnums::SHARED_LIBRARY:
3520
0
      linkerFlagsVar = "CMAKE_SHARED_LINKER_FLAGS";
3521
0
      break;
3522
0
    case cmStateEnums::MODULE_LIBRARY:
3523
0
      linkerFlagsVar = "CMAKE_MODULE_LINKER_FLAGS";
3524
0
      break;
3525
0
    default:
3526
0
      return;
3527
0
  }
3528
0
  this->AddConfigVariableFlags(flags, linkerFlagsVar, target,
3529
0
                               cmBuildStep::Link, lang, config);
3530
0
}
3531
3532
void cmLocalGenerator::AddTargetPropertyLinkFlags(
3533
  std::string& flags, cmGeneratorTarget const* target,
3534
  std::string const& config)
3535
0
{
3536
0
  cmValue targetLinkFlags = target->GetProperty("LINK_FLAGS");
3537
0
  if (targetLinkFlags) {
3538
0
    this->AppendFlags(flags, *targetLinkFlags);
3539
0
  }
3540
0
  if (!config.empty()) {
3541
0
    cmValue targetLinkFlagsConfig = target->GetProperty(
3542
0
      cmStrCat("LINK_FLAGS_", cmSystemTools::UpperCase(config)));
3543
0
    if (targetLinkFlagsConfig) {
3544
0
      this->AppendFlags(flags, *targetLinkFlagsConfig);
3545
0
    }
3546
0
  }
3547
0
}
3548
3549
void cmLocalGenerator::AppendIPOLinkerFlags(std::string& flags,
3550
                                            cmGeneratorTarget* target,
3551
                                            std::string const& config,
3552
                                            std::string const& lang)
3553
0
{
3554
0
  if (!target->IsIPOEnabled(lang, config)) {
3555
0
    return;
3556
0
  }
3557
3558
0
  switch (target->GetType()) {
3559
0
    case cmStateEnums::EXECUTABLE:
3560
0
    case cmStateEnums::SHARED_LIBRARY:
3561
0
    case cmStateEnums::MODULE_LIBRARY:
3562
0
      break;
3563
0
    default:
3564
0
      return;
3565
0
  }
3566
3567
0
  std::string const name = "CMAKE_" + lang + "_LINK_OPTIONS_IPO";
3568
0
  cmValue rawFlagsList = this->Makefile->GetDefinition(name);
3569
0
  if (!rawFlagsList) {
3570
0
    return;
3571
0
  }
3572
3573
0
  cmList flagsList{ *rawFlagsList };
3574
0
  for (std::string const& o : flagsList) {
3575
0
    this->AppendFlagEscape(flags, o);
3576
0
  }
3577
0
}
3578
3579
void cmLocalGenerator::AppendPositionIndependentLinkerFlags(
3580
  std::string& flags, cmGeneratorTarget* target, std::string const& config,
3581
  std::string const& lang)
3582
0
{
3583
  // For now, only EXECUTABLE is concerned
3584
0
  if (target->GetType() != cmStateEnums::EXECUTABLE) {
3585
0
    return;
3586
0
  }
3587
3588
0
  char const* PICValue = target->GetLinkPIEProperty(config);
3589
0
  if (!PICValue) {
3590
    // POSITION_INDEPENDENT_CODE is not set
3591
0
    return;
3592
0
  }
3593
3594
0
  std::string const mode = cmIsOn(PICValue) ? "PIE" : "NO_PIE";
3595
3596
0
  std::string supported = "CMAKE_" + lang + "_LINK_" + mode + "_SUPPORTED";
3597
0
  if (this->Makefile->GetDefinition(supported).IsOff()) {
3598
0
    return;
3599
0
  }
3600
3601
0
  std::string name = "CMAKE_" + lang + "_LINK_OPTIONS_" + mode;
3602
3603
0
  auto pieFlags = this->Makefile->GetSafeDefinition(name);
3604
0
  if (pieFlags.empty()) {
3605
0
    return;
3606
0
  }
3607
3608
0
  cmList flagsList{ pieFlags };
3609
0
  for (auto const& flag : flagsList) {
3610
0
    this->AppendFlagEscape(flags, flag);
3611
0
  }
3612
0
}
3613
3614
void cmLocalGenerator::AppendWarningAsErrorLinkerFlags(
3615
  std::string& flags, cmGeneratorTarget* target, std::string const& lang)
3616
0
{
3617
0
  if (this->GetCMakeInstance()->GetIgnoreLinkWarningAsError()) {
3618
0
    return;
3619
0
  }
3620
3621
0
  switch (target->GetType()) {
3622
0
    case cmStateEnums::EXECUTABLE:
3623
0
    case cmStateEnums::SHARED_LIBRARY:
3624
0
    case cmStateEnums::MODULE_LIBRARY:
3625
0
      break;
3626
0
    default:
3627
0
      return;
3628
0
  }
3629
3630
0
  auto const wError = target->GetProperty("LINK_WARNING_AS_ERROR");
3631
0
  if (wError.IsOff()) {
3632
0
    return;
3633
0
  }
3634
0
  cmList wErrorOptions;
3635
0
  if (wError.IsOn()) {
3636
0
    wErrorOptions = { "DRIVER", "LINKER" };
3637
0
  } else {
3638
0
    wErrorOptions = wError;
3639
0
    std::sort(wErrorOptions.begin(), wErrorOptions.end());
3640
0
    wErrorOptions.erase(
3641
0
      std::unique(wErrorOptions.begin(), wErrorOptions.end()),
3642
0
      wErrorOptions.end());
3643
0
  }
3644
3645
0
  auto linkModeIsDriver =
3646
0
    this->Makefile->GetDefinition(cmStrCat("CMAKE_", lang, "_LINK_MODE")) ==
3647
0
    "DRIVER"_s;
3648
0
  std::string errorMessage;
3649
0
  for (auto const& option : wErrorOptions) {
3650
0
    if (option != "DRIVER"_s && option != "LINKER"_s) {
3651
0
      errorMessage += cmStrCat("  ", option, '\n');
3652
0
      continue;
3653
0
    }
3654
3655
0
    if (option == "DRIVER"_s && !linkModeIsDriver) {
3656
0
      continue;
3657
0
    }
3658
3659
0
    auto const wErrorOpts = this->Makefile->GetDefinition(cmStrCat(
3660
0
      "CMAKE_", lang, '_', (option == "DRIVER"_s ? "COMPILE" : "LINK"),
3661
0
      "_OPTIONS_WARNING_AS_ERROR"));
3662
0
    if (wErrorOpts.IsSet()) {
3663
0
      auto items =
3664
0
        cmExpandListWithBacktrace(wErrorOpts, target->GetBacktrace());
3665
0
      if (option == "LINKER"_s) {
3666
0
        target->ResolveLinkerWrapper(items, lang);
3667
0
      }
3668
0
      for (auto const& item : items) {
3669
0
        this->AppendFlagEscape(flags, item.Value);
3670
0
      }
3671
0
    }
3672
0
  }
3673
0
  if (!errorMessage.empty()) {
3674
0
    this->Makefile->GetCMakeInstance()->IssueMessage(
3675
0
      MessageType::FATAL_ERROR,
3676
0
      cmStrCat(
3677
0
        "Erroneous value(s) for 'LINK_WARNING_AS_ERROR' property of target '",
3678
0
        target->GetName(), "':\n", errorMessage));
3679
0
  }
3680
0
}
3681
3682
void cmLocalGenerator::AppendDependencyInfoLinkerFlags(
3683
  std::string& flags, cmGeneratorTarget* target, std::string const& config,
3684
  std::string const& linkLanguage)
3685
0
{
3686
0
  if (!this->GetGlobalGenerator()->SupportsLinkerDependencyFile() ||
3687
0
      !target->HasLinkDependencyFile(config)) {
3688
0
    return;
3689
0
  }
3690
3691
0
  auto depFlag = *this->Makefile->GetDefinition(
3692
0
    cmStrCat("CMAKE_", linkLanguage, "_LINKER_DEPFILE_FLAGS"));
3693
0
  if (depFlag.empty()) {
3694
0
    return;
3695
0
  }
3696
3697
0
  auto depFile = this->ConvertToOutputFormat(
3698
0
    this->MaybeRelativeToWorkDir(this->GetLinkDependencyFile(target, config)),
3699
0
    cmOutputConverter::SHELL);
3700
0
  auto rulePlaceholderExpander = this->CreateRulePlaceholderExpander();
3701
0
  cmRulePlaceholderExpander::RuleVariables linkDepsVariables;
3702
0
  linkDepsVariables.DependencyFile = depFile.c_str();
3703
0
  rulePlaceholderExpander->ExpandRuleVariables(this, depFlag,
3704
0
                                               linkDepsVariables);
3705
0
  auto depFlags = cmExpandListWithBacktrace(depFlag);
3706
0
  target->ResolveLinkerWrapper(depFlags, linkLanguage);
3707
3708
0
  this->AppendFlags(flags, depFlags);
3709
0
}
3710
3711
std::string cmLocalGenerator::GetLinkDependencyFile(
3712
  cmGeneratorTarget* /*target*/, std::string const& /*config*/) const
3713
0
{
3714
0
  return "link.d";
3715
0
}
3716
3717
void cmLocalGenerator::AppendModuleDefinitionFlag(
3718
  std::string& flags, cmGeneratorTarget const* target,
3719
  cmLinkLineComputer* linkLineComputer, std::string const& config,
3720
  std::string const& lang)
3721
0
{
3722
0
  cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
3723
0
    target->GetModuleDefinitionInfo(config);
3724
0
  if (!mdi || mdi->DefFile.empty()) {
3725
0
    return;
3726
0
  }
3727
3728
0
  cmValue defFileFlag = this->Makefile->GetDefinition(
3729
0
    cmStrCat("CMAKE_", lang, "_LINK_DEF_FILE_FLAG"));
3730
0
  if (!defFileFlag) {
3731
0
    defFileFlag = this->Makefile->GetDefinition("CMAKE_LINK_DEF_FILE_FLAG");
3732
0
  }
3733
0
  if (!defFileFlag) {
3734
0
    return;
3735
0
  }
3736
3737
  // Append the flag and value.  Use ConvertToLinkReference to help
3738
  // vs6's "cl -link" pass it to the linker.
3739
0
  std::string flag =
3740
0
    cmStrCat(*defFileFlag,
3741
0
             this->ConvertToOutputFormat(
3742
0
               linkLineComputer->ConvertToLinkReference(mdi->DefFile),
3743
0
               cmOutputConverter::SHELL));
3744
0
  this->AppendFlags(flags, flag);
3745
0
}
3746
3747
bool cmLocalGenerator::AppendLWYUFlags(std::string& flags,
3748
                                       cmGeneratorTarget const* target,
3749
                                       std::string const& lang)
3750
0
{
3751
0
  auto useLWYU = target->GetPropertyAsBool("LINK_WHAT_YOU_USE") &&
3752
0
    (target->GetType() == cmStateEnums::TargetType::EXECUTABLE ||
3753
0
     target->GetType() == cmStateEnums::TargetType::SHARED_LIBRARY ||
3754
0
     target->GetType() == cmStateEnums::TargetType::MODULE_LIBRARY);
3755
3756
0
  if (useLWYU) {
3757
0
    auto const& lwyuFlag = this->GetMakefile()->GetSafeDefinition(
3758
0
      cmStrCat("CMAKE_", lang, "_LINK_WHAT_YOU_USE_FLAG"));
3759
0
    useLWYU = !lwyuFlag.empty();
3760
3761
0
    if (useLWYU) {
3762
0
      std::vector<BT<std::string>> lwyuOpts;
3763
0
      lwyuOpts.emplace_back(lwyuFlag);
3764
0
      this->AppendFlags(flags, target->ResolveLinkerWrapper(lwyuOpts, lang));
3765
0
    }
3766
0
  }
3767
3768
0
  return useLWYU;
3769
0
}
3770
3771
void cmLocalGenerator::AppendCompileOptions(std::string& options,
3772
                                            std::string const& options_list,
3773
                                            char const* regex) const
3774
0
{
3775
  // Short-circuit if there are no options.
3776
0
  if (options_list.empty()) {
3777
0
    return;
3778
0
  }
3779
3780
  // Expand the list of options.
3781
0
  cmList options_vec{ options_list };
3782
0
  this->AppendCompileOptions(options, options_vec, regex);
3783
0
}
3784
3785
void cmLocalGenerator::AppendCompileOptions(
3786
  std::string& options, std::vector<std::string> const& options_vec,
3787
  char const* regex) const
3788
0
{
3789
0
  if (regex) {
3790
    // Filter flags upon specified reges.
3791
0
    cmsys::RegularExpression r(regex);
3792
3793
0
    for (std::string const& opt : options_vec) {
3794
0
      if (r.find(opt)) {
3795
0
        this->AppendFlagEscape(options, opt);
3796
0
      }
3797
0
    }
3798
0
  } else {
3799
0
    for (std::string const& opt : options_vec) {
3800
0
      this->AppendFlagEscape(options, opt);
3801
0
    }
3802
0
  }
3803
0
}
3804
3805
void cmLocalGenerator::AppendCompileOptions(
3806
  std::vector<BT<std::string>>& options,
3807
  std::vector<BT<std::string>> const& options_vec, char const* regex) const
3808
0
{
3809
0
  if (regex) {
3810
    // Filter flags upon specified regular expressions.
3811
0
    cmsys::RegularExpression r(regex);
3812
3813
0
    for (BT<std::string> const& opt : options_vec) {
3814
0
      if (r.find(opt.Value)) {
3815
0
        std::string flag;
3816
0
        this->AppendFlagEscape(flag, opt.Value);
3817
0
        options.emplace_back(std::move(flag), opt.Backtrace);
3818
0
      }
3819
0
    }
3820
0
  } else {
3821
0
    for (BT<std::string> const& opt : options_vec) {
3822
0
      std::string flag;
3823
0
      this->AppendFlagEscape(flag, opt.Value);
3824
0
      options.emplace_back(std::move(flag), opt.Backtrace);
3825
0
    }
3826
0
  }
3827
0
}
3828
3829
void cmLocalGenerator::AppendIncludeDirectories(
3830
  std::vector<std::string>& includes, std::string const& includes_list,
3831
  cmSourceFile const& sourceFile) const
3832
0
{
3833
  // Short-circuit if there are no includes.
3834
0
  if (includes_list.empty()) {
3835
0
    return;
3836
0
  }
3837
3838
  // Expand the list of includes.
3839
0
  cmList includes_vec{ includes_list };
3840
0
  this->AppendIncludeDirectories(includes, includes_vec, sourceFile);
3841
0
}
3842
3843
void cmLocalGenerator::AppendIncludeDirectories(
3844
  std::vector<std::string>& includes,
3845
  std::vector<std::string> const& includes_vec,
3846
  cmSourceFile const& sourceFile) const
3847
0
{
3848
0
  std::unordered_set<std::string> uniqueIncludes;
3849
3850
0
  for (std::string const& include : includes_vec) {
3851
0
    if (!cmSystemTools::FileIsFullPath(include)) {
3852
0
      std::ostringstream e;
3853
0
      e << "Found relative path while evaluating include directories of "
3854
0
           "\""
3855
0
        << sourceFile.GetLocation().GetName() << "\":\n  \"" << include
3856
0
        << "\"\n";
3857
3858
0
      this->IssueMessage(MessageType::FATAL_ERROR, e.str());
3859
0
      return;
3860
0
    }
3861
3862
0
    std::string inc = include;
3863
3864
0
    if (!cmIsOff(inc)) {
3865
0
      cmSystemTools::ConvertToUnixSlashes(inc);
3866
0
    }
3867
3868
0
    if (uniqueIncludes.insert(inc).second) {
3869
0
      includes.push_back(std::move(inc));
3870
0
    }
3871
0
  }
3872
0
}
3873
3874
void cmLocalGenerator::AppendDefines(std::set<std::string>& defines,
3875
                                     std::string const& defines_list) const
3876
0
{
3877
0
  std::set<BT<std::string>> tmp;
3878
0
  this->AppendDefines(tmp, cmExpandListWithBacktrace(defines_list));
3879
0
  for (BT<std::string> const& i : tmp) {
3880
0
    defines.emplace(i.Value);
3881
0
  }
3882
0
}
3883
3884
void cmLocalGenerator::AppendDefines(std::set<BT<std::string>>& defines,
3885
                                     std::string const& defines_list) const
3886
0
{
3887
  // Short-circuit if there are no definitions.
3888
0
  if (defines_list.empty()) {
3889
0
    return;
3890
0
  }
3891
3892
  // Expand the list of definitions.
3893
0
  this->AppendDefines(defines, cmExpandListWithBacktrace(defines_list));
3894
0
}
3895
3896
void cmLocalGenerator::AppendDefines(
3897
  std::set<BT<std::string>>& defines,
3898
  std::vector<BT<std::string>> const& defines_vec) const
3899
0
{
3900
0
  for (BT<std::string> const& d : defines_vec) {
3901
    // Skip unsupported definitions.
3902
0
    if (!this->CheckDefinition(d.Value)) {
3903
0
      continue;
3904
0
    }
3905
    // remove any leading -D
3906
0
    if (cmHasLiteralPrefix(d.Value, "-D")) {
3907
0
      defines.emplace(d.Value.substr(2), d.Backtrace);
3908
0
    } else {
3909
0
      defines.insert(d);
3910
0
    }
3911
0
  }
3912
0
}
3913
3914
void cmLocalGenerator::JoinDefines(std::set<std::string> const& defines,
3915
                                   std::string& definesString,
3916
                                   std::string const& lang)
3917
0
{
3918
  // Lookup the define flag for the current language.
3919
0
  std::string dflag = "-D";
3920
0
  if (!lang.empty()) {
3921
0
    cmValue df =
3922
0
      this->Makefile->GetDefinition(cmStrCat("CMAKE_", lang, "_DEFINE_FLAG"));
3923
0
    if (cmNonempty(df)) {
3924
0
      dflag = *df;
3925
0
    }
3926
0
  }
3927
0
  char const* itemSeparator = definesString.empty() ? "" : " ";
3928
0
  for (std::string const& define : defines) {
3929
    // Append the definition with proper escaping.
3930
0
    std::string def = dflag;
3931
0
    if (this->GetState()->UseWatcomWMake()) {
3932
      // The Watcom compiler does its own command line parsing instead
3933
      // of using the windows shell rules.  Definitions are one of
3934
      //   -DNAME
3935
      //   -DNAME=<cpp-token>
3936
      //   -DNAME="c-string with spaces and other characters(?@#$)"
3937
      //
3938
      // Watcom will properly parse each of these cases from the
3939
      // command line without any escapes.  However we still have to
3940
      // get the '$' and '#' characters through WMake as '$$' and
3941
      // '$#'.
3942
0
      for (char c : define) {
3943
0
        if (c == '$' || c == '#') {
3944
0
          def += '$';
3945
0
        }
3946
0
        def += c;
3947
0
      }
3948
0
    } else {
3949
      // Make the definition appear properly on the command line.  Use
3950
      // -DNAME="value" instead of -D"NAME=value" for historical reasons.
3951
0
      std::string::size_type eq = define.find('=');
3952
0
      def += define.substr(0, eq);
3953
0
      if (eq != std::string::npos) {
3954
0
        def += "=";
3955
0
        def += this->EscapeForShell(define.substr(eq + 1), true);
3956
0
      }
3957
0
    }
3958
0
    definesString += itemSeparator;
3959
0
    itemSeparator = " ";
3960
0
    definesString += def;
3961
0
  }
3962
0
}
3963
3964
void cmLocalGenerator::AppendFeatureOptions(std::string& flags,
3965
                                            std::string const& lang,
3966
                                            char const* feature)
3967
0
{
3968
0
  cmValue optionList = this->Makefile->GetDefinition(
3969
0
    cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_", feature));
3970
0
  if (optionList) {
3971
0
    cmList options{ *optionList };
3972
0
    for (std::string const& o : options) {
3973
0
      this->AppendFlagEscape(flags, o);
3974
0
    }
3975
0
  }
3976
0
}
3977
3978
cmValue cmLocalGenerator::GetFeature(std::string const& feature,
3979
                                     std::string const& config)
3980
0
{
3981
0
  std::string featureName = feature;
3982
  // TODO: Define accumulation policy for features (prepend, append,
3983
  // replace). Currently we always replace.
3984
0
  if (!config.empty()) {
3985
0
    featureName += "_";
3986
0
    featureName += cmSystemTools::UpperCase(config);
3987
0
  }
3988
0
  cmStateSnapshot snp = this->StateSnapshot;
3989
0
  while (snp.IsValid()) {
3990
0
    if (cmValue value = snp.GetDirectory().GetProperty(featureName)) {
3991
0
      return value;
3992
0
    }
3993
0
    snp = snp.GetBuildsystemDirectoryParent();
3994
0
  }
3995
0
  return nullptr;
3996
0
}
3997
3998
std::string cmLocalGenerator::GetProjectName() const
3999
0
{
4000
0
  return this->StateSnapshot.GetProjectName();
4001
0
}
4002
4003
std::string cmLocalGenerator::ConstructComment(
4004
  cmCustomCommandGenerator const& ccg, char const* default_comment) const
4005
0
{
4006
  // Check for a comment provided with the command.
4007
0
  if (cm::optional<std::string> comment = ccg.GetComment()) {
4008
0
    return *comment;
4009
0
  }
4010
4011
  // Construct a reasonable default comment if possible.
4012
0
  if (!ccg.GetOutputs().empty()) {
4013
0
    std::string comment;
4014
0
    comment = "Generating ";
4015
0
    char const* sep = "";
4016
0
    for (std::string const& o : ccg.GetOutputs()) {
4017
0
      comment += sep;
4018
0
      comment += this->MaybeRelativeToCurBinDir(o);
4019
0
      sep = ", ";
4020
0
    }
4021
0
    return comment;
4022
0
  }
4023
4024
  // Otherwise use the provided default.
4025
0
  return default_comment;
4026
0
}
4027
4028
class cmInstallTargetGeneratorLocal : public cmInstallTargetGenerator
4029
{
4030
public:
4031
  cmInstallTargetGeneratorLocal(cmLocalGenerator* lg, std::string const& t,
4032
                                std::string const& dest, bool implib)
4033
0
    : cmInstallTargetGenerator(
4034
0
        t, dest, implib, "", std::vector<std::string>(), "Unspecified",
4035
0
        cmInstallGenerator::SelectMessageLevel(lg->GetMakefile()), false,
4036
0
        false)
4037
0
  {
4038
0
    this->Compute(lg);
4039
0
  }
4040
};
4041
4042
void cmLocalGenerator::GenerateTargetInstallRules(
4043
  std::ostream& os, std::string const& config,
4044
  std::vector<std::string> const& configurationTypes)
4045
0
{
4046
  // Convert the old-style install specification from each target to
4047
  // an install generator and run it.
4048
0
  auto const& tgts = this->GetGeneratorTargets();
4049
0
  for (auto const& l : tgts) {
4050
0
    if (l->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
4051
0
      continue;
4052
0
    }
4053
4054
    // Include the user-specified pre-install script for this target.
4055
0
    if (cmValue preinstall = l->GetProperty("PRE_INSTALL_SCRIPT")) {
4056
0
      cmInstallScriptGenerator g(*preinstall, false, "", false, false);
4057
0
      g.Generate(os, config, configurationTypes);
4058
0
    }
4059
4060
    // Install this target if a destination is given.
4061
0
    if (!l->Target->GetInstallPath().empty()) {
4062
      // Compute the full install destination.  Note that converting
4063
      // to unix slashes also removes any trailing slash.
4064
      // We also skip over the leading slash given by the user.
4065
0
      std::string destination = l->Target->GetInstallPath().substr(1);
4066
0
      cmSystemTools::ConvertToUnixSlashes(destination);
4067
0
      if (destination.empty()) {
4068
0
        destination = ".";
4069
0
      }
4070
4071
      // Generate the proper install generator for this target type.
4072
0
      switch (l->GetType()) {
4073
0
        case cmStateEnums::EXECUTABLE:
4074
0
        case cmStateEnums::STATIC_LIBRARY:
4075
0
        case cmStateEnums::MODULE_LIBRARY: {
4076
          // Use a target install generator.
4077
0
          cmInstallTargetGeneratorLocal g(this, l->GetName(), destination,
4078
0
                                          false);
4079
0
          g.Generate(os, config, configurationTypes);
4080
0
        } break;
4081
0
        case cmStateEnums::SHARED_LIBRARY: {
4082
#if defined(_WIN32) || defined(__CYGWIN__)
4083
          // Special code to handle DLL.  Install the import library
4084
          // to the normal destination and the DLL to the runtime
4085
          // destination.
4086
          cmInstallTargetGeneratorLocal g1(this, l->GetName(), destination,
4087
                                           true);
4088
          g1.Generate(os, config, configurationTypes);
4089
          // We also skip over the leading slash given by the user.
4090
          destination = l->Target->GetRuntimeInstallPath().substr(1);
4091
          cmSystemTools::ConvertToUnixSlashes(destination);
4092
          cmInstallTargetGeneratorLocal g2(this, l->GetName(), destination,
4093
                                           false);
4094
          g2.Generate(os, config, configurationTypes);
4095
#else
4096
          // Use a target install generator.
4097
0
          cmInstallTargetGeneratorLocal g(this, l->GetName(), destination,
4098
0
                                          false);
4099
0
          g.Generate(os, config, configurationTypes);
4100
0
#endif
4101
0
        } break;
4102
0
        default:
4103
0
          break;
4104
0
      }
4105
0
    }
4106
4107
    // Include the user-specified post-install script for this target.
4108
0
    if (cmValue postinstall = l->GetProperty("POST_INSTALL_SCRIPT")) {
4109
0
      cmInstallScriptGenerator g(*postinstall, false, "", false, false);
4110
0
      g.Generate(os, config, configurationTypes);
4111
0
    }
4112
0
  }
4113
0
}
4114
4115
namespace {
4116
bool cmLocalGeneratorShortenObjectName(std::string& objName,
4117
                                       std::string::size_type max_len)
4118
0
{
4119
  // Check if the path can be shortened using an md5 sum replacement for
4120
  // a portion of the path.
4121
0
  std::string::size_type md5Len = 32;
4122
0
  std::string::size_type numExtraChars = objName.size() - max_len + md5Len;
4123
0
  std::string::size_type pos = objName.find('/', numExtraChars);
4124
0
  if (pos == std::string::npos) {
4125
0
    pos = objName.rfind('/', numExtraChars);
4126
0
    if (pos == std::string::npos || pos <= md5Len) {
4127
0
      return false;
4128
0
    }
4129
0
  }
4130
4131
  // Replace the beginning of the path portion of the object name with
4132
  // its own md5 sum.
4133
0
  cmCryptoHash md5(cmCryptoHash::AlgoMD5);
4134
0
  std::string md5name = cmStrCat(md5.HashString(objName.substr(0, pos)),
4135
0
                                 cm::string_view(objName).substr(pos));
4136
0
  objName = md5name;
4137
4138
  // The object name is now shorter, check if it is short enough.
4139
0
  return pos >= numExtraChars;
4140
0
}
4141
4142
bool cmLocalGeneratorCheckObjectName(std::string& objName,
4143
                                     std::string::size_type dir_len,
4144
                                     std::string::size_type max_total_len)
4145
0
{
4146
  // Enforce the maximum file name length if possible.
4147
0
  std::string::size_type max_obj_len = max_total_len;
4148
0
  if (dir_len < max_total_len) {
4149
0
    max_obj_len = max_total_len - dir_len;
4150
0
    if (objName.size() > max_obj_len) {
4151
      // The current object file name is too long.  Try to shorten it.
4152
0
      return cmLocalGeneratorShortenObjectName(objName, max_obj_len);
4153
0
    }
4154
    // The object file name is short enough.
4155
0
    return true;
4156
0
  }
4157
  // The build directory in which the object will be stored is
4158
  // already too deep.
4159
0
  return false;
4160
0
}
4161
}
4162
4163
std::string cmLocalGenerator::CreateSafeObjectFileName(
4164
  std::string const& sin) const
4165
0
{
4166
  // Start with the original name.
4167
0
  std::string ssin = sin;
4168
4169
  // Avoid full paths by removing leading slashes.
4170
0
  ssin.erase(0, ssin.find_first_not_of('/'));
4171
4172
  // Avoid full paths by removing colons.
4173
0
  std::replace(ssin.begin(), ssin.end(), ':', '_');
4174
4175
  // Avoid relative paths that go up the tree.
4176
0
  cmSystemTools::ReplaceString(ssin, "../", "__/");
4177
4178
  // Avoid spaces.
4179
0
  std::replace(ssin.begin(), ssin.end(), ' ', '_');
4180
4181
0
  return ssin;
4182
0
}
4183
4184
void cmLocalGenerator::ComputeSourceGroupSearchIndex()
4185
0
{
4186
0
#if !defined(CMAKE_BOOTSTRAP)
4187
0
  SourceGroupVector const& sourceGroups = this->Makefile->GetSourceGroups();
4188
4189
  // Build lookup index from sources to source groups
4190
0
  std::queue<cmSourceGroup*> sgToVisit;
4191
0
  for (auto const& group : sourceGroups) {
4192
0
    cmSourceGroup* cmSourceGroup = group.get();
4193
0
    sgToVisit.emplace(cmSourceGroup);
4194
0
  }
4195
4196
0
  while (!sgToVisit.empty()) {
4197
0
    cmSourceGroup* sourceGroup = sgToVisit.front();
4198
0
    sgToVisit.pop();
4199
0
    for (auto const& sgChild : sourceGroup->GetGroupChildren()) {
4200
0
      sgToVisit.emplace(sgChild.get());
4201
0
    }
4202
0
    for (std::string const& source : sourceGroup->GetGroupFiles()) {
4203
0
      this->SourceGroupSearchIndex.emplace(source, sourceGroup);
4204
0
    }
4205
0
  }
4206
0
#endif
4207
0
}
4208
4209
cmSourceGroup* cmLocalGenerator::FindSourceGroup(std::string const& source)
4210
0
{
4211
0
#if !defined(CMAKE_BOOTSTRAP)
4212
0
  auto const indexIt = SourceGroupSearchIndex.find(source);
4213
0
  if (indexIt != SourceGroupSearchIndex.cend()) {
4214
0
    if (cmSourceGroup* result = indexIt->second) {
4215
0
      return result;
4216
0
    }
4217
0
  }
4218
4219
0
  cmSourceGroup* sourceGroup =
4220
0
    cmSourceGroup::FindSourceGroup(source, this->Makefile->GetSourceGroups());
4221
0
  if (sourceGroup) {
4222
    // Update index if we have a miss
4223
0
    SourceGroupSearchIndex.emplace(source, sourceGroup);
4224
0
  }
4225
0
  return sourceGroup;
4226
#else
4227
  static_cast<void>(source);
4228
  return nullptr;
4229
#endif
4230
0
}
4231
4232
std::string& cmLocalGenerator::CreateSafeUniqueObjectFileName(
4233
  std::string const& sin, std::string const& dir_max)
4234
0
{
4235
  // Look for an existing mapped name for this object file.
4236
0
  auto it = this->UniqueObjectNamesMap.find(sin);
4237
4238
  // If no entry exists create one.
4239
0
  if (it == this->UniqueObjectNamesMap.end()) {
4240
0
    auto ssin = this->CreateSafeObjectFileName(sin);
4241
4242
    // Mangle the name if necessary.
4243
0
    if (this->Makefile->IsOn("CMAKE_MANGLE_OBJECT_FILE_NAMES")) {
4244
0
      bool done;
4245
0
      int cc = 0;
4246
0
      char rpstr[100];
4247
0
      snprintf(rpstr, sizeof(rpstr), "_p_");
4248
0
      cmSystemTools::ReplaceString(ssin, "+", rpstr);
4249
0
      std::string sssin = sin;
4250
0
      do {
4251
0
        done = true;
4252
0
        for (it = this->UniqueObjectNamesMap.begin();
4253
0
             it != this->UniqueObjectNamesMap.end(); ++it) {
4254
0
          if (it->second == ssin) {
4255
0
            done = false;
4256
0
          }
4257
0
        }
4258
0
        if (done) {
4259
0
          break;
4260
0
        }
4261
0
        sssin = ssin;
4262
0
        cmSystemTools::ReplaceString(ssin, "_p_", rpstr);
4263
0
        snprintf(rpstr, sizeof(rpstr), "_p%d_", cc++);
4264
0
      } while (!done);
4265
0
    }
4266
4267
0
    if (!cmLocalGeneratorCheckObjectName(ssin, dir_max.size(),
4268
0
                                         this->ObjectPathMax)) {
4269
      // Warn if this is the first time the path has been seen.
4270
0
      if (this->ObjectMaxPathViolations.insert(dir_max).second) {
4271
0
        std::ostringstream m;
4272
        /* clang-format off */
4273
0
        m << "The object file directory\n"
4274
0
          << "  " << dir_max << "\n"
4275
0
          << "has " << dir_max.size() << " characters.  "
4276
0
          << "The maximum full path to an object file is "
4277
0
          << this->ObjectPathMax << " characters "
4278
0
          << "(see CMAKE_OBJECT_PATH_MAX).  "
4279
0
          << "Object file\n"
4280
0
          << "  " << ssin << "\n"
4281
0
          << "cannot be safely placed under this directory.  "
4282
0
          << "The build may not work correctly.";
4283
        /* clang-format on */
4284
0
        this->IssueMessage(MessageType::WARNING, m.str());
4285
0
      }
4286
0
    }
4287
4288
    // Insert the newly mapped object file name.
4289
0
    std::map<std::string, std::string>::value_type e(sin, ssin);
4290
0
    it = this->UniqueObjectNamesMap.insert(e).first;
4291
0
  }
4292
4293
  // Return the map entry.
4294
0
  return it->second;
4295
0
}
4296
4297
void cmLocalGenerator::ComputeObjectFilenames(
4298
  std::map<cmSourceFile const*, cmObjectLocations>& /*unused*/,
4299
  std::string const& /*unused*/, cmGeneratorTarget const* /*unused*/)
4300
0
{
4301
0
}
4302
4303
bool cmLocalGenerator::IsWindowsShell() const
4304
0
{
4305
0
  return this->GetState()->UseWindowsShell();
4306
0
}
4307
4308
bool cmLocalGenerator::IsWatcomWMake() const
4309
0
{
4310
0
  return this->GetState()->UseWatcomWMake();
4311
0
}
4312
4313
bool cmLocalGenerator::IsMinGWMake() const
4314
0
{
4315
0
  return this->GetState()->UseMinGWMake();
4316
0
}
4317
4318
bool cmLocalGenerator::IsNMake() const
4319
0
{
4320
0
  return this->GetState()->UseNMake();
4321
0
}
4322
4323
bool cmLocalGenerator::IsNinjaMulti() const
4324
0
{
4325
0
  return this->GetState()->UseNinjaMulti();
4326
0
}
4327
4328
bool cmLocalGenerator::IsWindowsVSIDE() const
4329
0
{
4330
0
  return this->GetState()->UseWindowsVSIDE();
4331
0
}
4332
4333
namespace {
4334
std::string relativeIfUnder(std::string const& top, std::string const& cur,
4335
                            std::string const& path)
4336
0
{
4337
  // Use a path relative to 'cur' if it can be expressed without
4338
  // a `../` sequence that leaves 'top'.
4339
0
  if (cmSystemTools::IsSubDirectory(path, cur) ||
4340
0
      (cmSystemTools::IsSubDirectory(cur, top) &&
4341
0
       cmSystemTools::IsSubDirectory(path, top))) {
4342
0
    return cmSystemTools::ForceToRelativePath(cur, path);
4343
0
  }
4344
0
  return path;
4345
0
}
4346
}
4347
4348
std::string cmLocalGenerator::GetRelativeSourceFileName(
4349
  cmSourceFile const& source) const
4350
0
{
4351
  // Construct the object file name using the full path to the source
4352
  // file which is its only unique identification.
4353
0
  std::string const& fullPath = source.GetFullPath();
4354
4355
  // Try referencing the source relative to the source tree.
4356
0
  std::string relFromSource = relativeIfUnder(
4357
0
    this->GetSourceDirectory(), this->GetCurrentSourceDirectory(), fullPath);
4358
0
  assert(!relFromSource.empty());
4359
0
  bool relSource = !cmSystemTools::FileIsFullPath(relFromSource);
4360
0
  bool subSource = relSource && relFromSource[0] != '.';
4361
4362
  // Try referencing the source relative to the binary tree.
4363
0
  std::string relFromBinary = relativeIfUnder(
4364
0
    this->GetBinaryDirectory(), this->GetCurrentBinaryDirectory(), fullPath);
4365
0
  assert(!relFromBinary.empty());
4366
0
  bool relBinary = !cmSystemTools::FileIsFullPath(relFromBinary);
4367
0
  bool subBinary = relBinary && relFromBinary[0] != '.';
4368
4369
  // Select a nice-looking reference to the source file to construct
4370
  // the object file name.
4371
0
  std::string objectName;
4372
  // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
4373
  // NOLINTNEXTLINE(bugprone-branch-clone)
4374
0
  if ((relSource && !relBinary) || (subSource && !subBinary)) {
4375
0
    objectName = relFromSource;
4376
0
  } else if ((relBinary && !relSource) || (subBinary && !subSource) ||
4377
0
             relFromBinary.length() < relFromSource.length()) {
4378
0
    objectName = relFromBinary;
4379
0
  } else {
4380
0
    objectName = relFromSource;
4381
0
  }
4382
0
  return objectName;
4383
0
}
4384
4385
std::string cmLocalGenerator::GetCustomObjectFileName(
4386
  cmSourceFile const& source) const
4387
0
{
4388
0
  if (!this->GetGlobalGenerator()->SupportsCustomObjectNames()) {
4389
0
    return std::string{};
4390
0
  }
4391
4392
0
  if (auto objName = source.GetProperty("OBJECT_NAME")) {
4393
0
    cmGeneratorExpression ge(*this->GetCMakeInstance());
4394
0
    auto cge = ge.Parse(objName);
4395
0
    static std::string const INVALID_GENEX =
4396
0
      "_cmake_invalid_object_name_genex";
4397
0
    static std::string const INVALID_VALUE =
4398
0
      "_cmake_invalid_object_name_value";
4399
4400
0
    if (!cge) {
4401
0
      this->Makefile->IssueMessage(
4402
0
        MessageType::FATAL_ERROR,
4403
0
        cmStrCat("The  \"OBJECT_NAME\" property for\n  ", source.GetFullPath(),
4404
0
                 "\nis not a valid generator expression (", objName, ")."));
4405
0
      return INVALID_GENEX;
4406
0
    }
4407
0
    if (cge->GetHadHeadSensitiveCondition()) {
4408
      // Not reachable; all target-sensitive  genexes actually fail to parse.
4409
0
      this->Makefile->IssueMessage(
4410
0
        MessageType::FATAL_ERROR,
4411
0
        cmStrCat("The  \"OBJECT_NAME\" property for\n  ", source.GetFullPath(),
4412
0
                 "\ncontains a condition that queries the consuming target "
4413
0
                 "which is not supported (",
4414
0
                 objName, ")."));
4415
0
      return INVALID_GENEX;
4416
0
    }
4417
0
    if (cge->GetHadLinkLanguageSensitiveCondition()) {
4418
      // Not reachable; all target-sensitive  genexes actually fail to parse.
4419
0
      this->Makefile->IssueMessage(
4420
0
        MessageType::FATAL_ERROR,
4421
0
        cmStrCat("The  \"OBJECT_NAME\" property for\n  ", source.GetFullPath(),
4422
0
                 "\ncontains a condition that queries the link language "
4423
0
                 "which is not supported (",
4424
0
                 objName, ")."));
4425
0
      return INVALID_GENEX;
4426
0
    }
4427
4428
0
    auto objNameValue = cge->Evaluate(this, "");
4429
0
    if (cge->GetHadContextSensitiveCondition()) {
4430
0
      this->Makefile->IssueMessage(
4431
0
        MessageType::FATAL_ERROR,
4432
0
        cmStrCat("The  \"OBJECT_NAME\" property for\n  ", source.GetFullPath(),
4433
0
                 "\ncontains a context-sensitive condition which is not "
4434
0
                 "supported (",
4435
0
                 objName, ")."));
4436
0
      return INVALID_GENEX;
4437
0
    }
4438
4439
    // Skip if it evaluates to empty.
4440
0
    if (!objNameValue.empty()) {
4441
0
      cmCMakePath objNamePath = objNameValue;
4442
      // Verify that it is a relative path.
4443
0
      if (objNamePath.IsAbsolute()) {
4444
0
        this->Makefile->IssueMessage(
4445
0
          MessageType::FATAL_ERROR,
4446
0
          cmStrCat(
4447
0
            "The  \"OBJECT_NAME\" property for\n  ", source.GetFullPath(),
4448
0
            "\nresolves to an absolute path which is not supported:\n  ",
4449
0
            objNameValue));
4450
0
        return INVALID_VALUE;
4451
0
      }
4452
0
      auto isInvalidComponent = [](cmCMakePath const& component) -> bool {
4453
0
        return component == ".."_s;
4454
0
      };
4455
      // Verify that it contains no `..` components.
4456
0
      if (std::any_of(objNamePath.begin(), objNamePath.end(),
4457
0
                      isInvalidComponent)) {
4458
0
        this->Makefile->IssueMessage(
4459
0
          MessageType::FATAL_ERROR,
4460
0
          cmStrCat("The  \"OBJECT_NAME\" property for\n  ",
4461
0
                   source.GetFullPath(), "\ncontains an invalid component (",
4462
0
                   objNameValue, ")."));
4463
0
        return INVALID_VALUE;
4464
0
      }
4465
4466
0
      return objNameValue;
4467
0
    }
4468
0
  }
4469
4470
0
  return std::string{};
4471
0
}
4472
4473
std::string cmLocalGenerator::GetCustomInstallObjectFileName(
4474
  cmSourceFile const& source, std::string const& config,
4475
  char const* custom_ext) const
4476
0
{
4477
0
  if (auto objName = source.GetProperty("INSTALL_OBJECT_NAME")) {
4478
0
    cmGeneratorExpression ge(*this->GetCMakeInstance());
4479
0
    auto cge = ge.Parse(objName);
4480
0
    static std::string const INVALID_GENEX =
4481
0
      "_cmake_invalid_object_name_genex";
4482
0
    static std::string const INVALID_VALUE =
4483
0
      "_cmake_invalid_object_name_value";
4484
4485
0
    if (!cge) {
4486
0
      this->Makefile->IssueMessage(
4487
0
        MessageType::FATAL_ERROR,
4488
0
        cmStrCat("The  \"INSTALL_OBJECT_NAME\" property for\n  ",
4489
0
                 source.GetFullPath(),
4490
0
                 "\nis not a valid generator expression (", objName, ")."));
4491
0
      return INVALID_GENEX;
4492
0
    }
4493
0
    if (cge->GetHadHeadSensitiveCondition()) {
4494
      // Not reachable; all target-sensitive  genexes actually fail to parse.
4495
0
      this->Makefile->IssueMessage(
4496
0
        MessageType::FATAL_ERROR,
4497
0
        cmStrCat("The  \"INSTALL_OBJECT_NAME\" property for\n  ",
4498
0
                 source.GetFullPath(),
4499
0
                 "\ncontains a condition that queries the consuming target "
4500
0
                 "which is not supported (",
4501
0
                 objName, ")."));
4502
0
      return INVALID_GENEX;
4503
0
    }
4504
0
    if (cge->GetHadLinkLanguageSensitiveCondition()) {
4505
      // Not reachable; all target-sensitive  genexes actually fail to parse.
4506
0
      this->Makefile->IssueMessage(
4507
0
        MessageType::FATAL_ERROR,
4508
0
        cmStrCat("The  \"INSTALL_OBJECT_NAME\" property for\n  ",
4509
0
                 source.GetFullPath(),
4510
0
                 "\ncontains a condition that queries the link language "
4511
0
                 "which is not supported (",
4512
0
                 objName, ")."));
4513
0
      return INVALID_GENEX;
4514
0
    }
4515
4516
0
    auto objNameValue = cge->Evaluate(this, config);
4517
4518
    // Skip if it evaluates to empty.
4519
0
    if (!objNameValue.empty()) {
4520
0
      cmCMakePath objNamePath = objNameValue;
4521
      // Verify that it is a relative path.
4522
0
      if (objNamePath.IsAbsolute()) {
4523
0
        this->Makefile->IssueMessage(
4524
0
          MessageType::FATAL_ERROR,
4525
0
          cmStrCat(
4526
0
            "The  \"INSTALL_OBJECT_NAME\" property for\n  ",
4527
0
            source.GetFullPath(),
4528
0
            "\nresolves to an absolute path which is not supported:\n  ",
4529
0
            objNameValue));
4530
0
        return INVALID_VALUE;
4531
0
      }
4532
0
      auto isInvalidComponent = [](cmCMakePath const& component) -> bool {
4533
0
        return component == ".."_s;
4534
0
      };
4535
      // Verify that it contains no `..` components.
4536
0
      if (std::any_of(objNamePath.begin(), objNamePath.end(),
4537
0
                      isInvalidComponent)) {
4538
0
        this->Makefile->IssueMessage(
4539
0
          MessageType::FATAL_ERROR,
4540
0
          cmStrCat("The  \"INSTALL_OBJECT_NAME\" property for\n  ",
4541
0
                   source.GetFullPath(), "\ncontains an invalid component (",
4542
0
                   objNameValue, ")."));
4543
0
        return INVALID_VALUE;
4544
0
      }
4545
4546
0
      if (custom_ext) {
4547
0
        objNameValue += custom_ext;
4548
0
      } else {
4549
0
        objNameValue +=
4550
0
          this->GetGlobalGenerator()->GetLanguageOutputExtension(source);
4551
0
      }
4552
4553
0
      return objNameValue;
4554
0
    }
4555
0
  }
4556
4557
0
  return std::string{};
4558
0
}
4559
4560
void cmLocalGenerator::FillCustomInstallObjectLocations(
4561
  cmSourceFile const& source, std::string const& config,
4562
  char const* custom_ext,
4563
  std::map<std::string, cmObjectLocation>& mapping) const
4564
0
{
4565
0
  auto installLoc =
4566
0
    this->GetCustomInstallObjectFileName(source, config, custom_ext);
4567
0
  if (!installLoc.empty()) {
4568
0
    mapping[config] = installLoc;
4569
0
  }
4570
0
}
4571
4572
std::string cmLocalGenerator::GetObjectFileNameWithoutTarget(
4573
  cmSourceFile const& source, std::string const& dir_max,
4574
  bool* hasSourceExtension, char const* customOutputExtension,
4575
  bool const* forceShortObjectName)
4576
0
{
4577
0
  bool useShortObjectNames = this->UseShortObjectNames();
4578
0
  if (forceShortObjectName) {
4579
0
    useShortObjectNames = *forceShortObjectName;
4580
0
  }
4581
4582
0
  if (!useShortObjectNames &&
4583
0
      this->GetGlobalGenerator()->SupportsCustomObjectNames()) {
4584
0
    auto customName = this->GetCustomObjectFileName(source);
4585
0
    if (!customName.empty()) {
4586
0
      auto ext = this->GlobalGenerator->GetLanguageOutputExtension(source);
4587
0
      if (customOutputExtension) {
4588
0
        ext = *customOutputExtension;
4589
0
      }
4590
0
      return cmStrCat(customName, ext);
4591
0
    }
4592
0
  }
4593
4594
  // This can return an absolute path in the case where source is
4595
  // not relative to the current source or binary directories
4596
0
  std::string objectName = this->GetRelativeSourceFileName(source);
4597
  // if it is still a full path check for the try compile case
4598
  // try compile never have in source sources, and should not
4599
  // have conflicting source file names in the same target
4600
0
  if (cmSystemTools::FileIsFullPath(objectName)) {
4601
0
    if (this->GetGlobalGenerator()->GetCMakeInstance()->GetIsInTryCompile()) {
4602
0
      objectName = cmSystemTools::GetFilenameName(source.GetFullPath());
4603
0
    }
4604
0
  }
4605
0
  bool const isPchObject = source.IsPchHeader() || source.IsPchSource();
4606
4607
  // Short object path policy selected, use as little info as necessary to
4608
  // select an object name
4609
0
  bool keptSourceExtension = true;
4610
0
  if (useShortObjectNames) {
4611
0
    objectName = this->GetShortObjectFileName(source);
4612
0
    keptSourceExtension = false;
4613
0
  }
4614
4615
  // Ensure that for the CMakeFiles/<target>.dir/generated_source_file
4616
  // we don't end up having:
4617
  // CMakeFiles/<target>.dir/CMakeFiles/<target>.dir/generated_source_file.obj
4618
0
  cmValue unitySourceFile = source.GetProperty("UNITY_SOURCE_FILE");
4619
0
  cmValue pchExtension = source.GetProperty("PCH_EXTENSION");
4620
0
  if (unitySourceFile || pchExtension || isPchObject) {
4621
0
    if (pchExtension) {
4622
0
      customOutputExtension = pchExtension->c_str();
4623
0
    }
4624
4625
0
    cmsys::RegularExpression var("(CMakeFiles/[^/]+.dir/)");
4626
0
    if (var.find(objectName)) {
4627
0
      objectName.erase(var.start(), var.end() - var.start());
4628
0
    }
4629
0
  }
4630
4631
  // Replace the original source file extension with the object file
4632
  // extension.
4633
0
  if (!source.GetPropertyAsBool("KEEP_EXTENSION")) {
4634
    // Decide whether this language wants to replace the source
4635
    // extension with the object extension.
4636
0
    bool replaceExt = false;
4637
0
    std::string lang = source.GetLanguage();
4638
0
    if (!lang.empty()) {
4639
0
      replaceExt = this->Makefile->IsOn(
4640
0
        cmStrCat("CMAKE_", lang, "_OUTPUT_EXTENSION_REPLACE"));
4641
0
    }
4642
4643
    // Remove the source extension if it is to be replaced.
4644
0
    if (replaceExt || customOutputExtension) {
4645
0
      keptSourceExtension = false;
4646
0
      std::string::size_type dot_pos = objectName.rfind('.');
4647
0
      if (dot_pos != std::string::npos) {
4648
0
        objectName = objectName.substr(0, dot_pos);
4649
0
      }
4650
0
    }
4651
4652
    // Strip source file extension when shortening object file paths
4653
0
    if (useShortObjectNames) {
4654
0
      objectName = cmSystemTools::GetFilenameWithoutExtension(objectName);
4655
0
    }
4656
    // Store the new extension.
4657
0
    if (customOutputExtension) {
4658
0
      objectName += customOutputExtension;
4659
0
    } else {
4660
0
      objectName += this->GlobalGenerator->GetLanguageOutputExtension(source);
4661
0
    }
4662
0
  }
4663
0
  if (hasSourceExtension) {
4664
0
    *hasSourceExtension = keptSourceExtension;
4665
0
  }
4666
4667
  // Convert to a safe name.
4668
0
  return this->CreateSafeUniqueObjectFileName(objectName, dir_max);
4669
0
}
4670
4671
bool cmLocalGenerator::UseShortObjectNames(
4672
  cmStateEnums::IntermediateDirKind kind) const
4673
0
{
4674
0
  return this->GlobalGenerator->UseShortObjectNames(kind);
4675
0
}
4676
4677
std::string cmLocalGenerator::GetObjectOutputRoot(
4678
  cmStateEnums::IntermediateDirKind kind) const
4679
0
{
4680
0
  if (this->UseShortObjectNames(kind)) {
4681
0
    return cmStrCat(this->GetCurrentBinaryDirectory(), '/',
4682
0
                    this->GlobalGenerator->GetShortBinaryOutputDir());
4683
0
  }
4684
0
  return cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles");
4685
0
}
4686
4687
bool cmLocalGenerator::AlwaysUsesCMFPaths() const
4688
0
{
4689
0
  return true;
4690
0
}
4691
4692
std::string cmLocalGenerator::GetShortObjectFileName(
4693
  cmSourceFile const& source) const
4694
0
{
4695
0
  std::string objectName = this->GetRelativeSourceFileName(source);
4696
0
  cmCryptoHash objNameHasher(cmCryptoHash::AlgoSHA3_512);
4697
0
  std::string terseObjectName =
4698
0
    objNameHasher.HashString(objectName).substr(0, 8);
4699
0
  return terseObjectName;
4700
0
}
4701
4702
std::string cmLocalGenerator::ComputeShortTargetDirectory(
4703
  cmGeneratorTarget const* target) const
4704
0
{
4705
0
  auto const& tgtName = target->GetName();
4706
0
  return this->GetGlobalGenerator()->ComputeTargetShortName(
4707
0
    this->GetCurrentBinaryDirectory(), tgtName);
4708
0
}
4709
4710
std::string cmLocalGenerator::GetSourceFileLanguage(cmSourceFile const& source)
4711
0
{
4712
0
  return source.GetLanguage();
4713
0
}
4714
4715
cmake* cmLocalGenerator::GetCMakeInstance() const
4716
0
{
4717
0
  return this->GlobalGenerator->GetCMakeInstance();
4718
0
}
4719
4720
std::string const& cmLocalGenerator::GetSourceDirectory() const
4721
0
{
4722
0
  return this->GetCMakeInstance()->GetHomeDirectory();
4723
0
}
4724
4725
std::string const& cmLocalGenerator::GetBinaryDirectory() const
4726
0
{
4727
0
  return this->GetCMakeInstance()->GetHomeOutputDirectory();
4728
0
}
4729
4730
std::string const& cmLocalGenerator::GetCurrentBinaryDirectory() const
4731
0
{
4732
0
  return this->StateSnapshot.GetDirectory().GetCurrentBinary();
4733
0
}
4734
4735
std::string const& cmLocalGenerator::GetCurrentSourceDirectory() const
4736
0
{
4737
0
  return this->StateSnapshot.GetDirectory().GetCurrentSource();
4738
0
}
4739
4740
std::string cmLocalGenerator::GetTargetDirectory(
4741
  cmGeneratorTarget const* /*unused*/,
4742
  cmStateEnums::IntermediateDirKind /*kind*/) const
4743
0
{
4744
0
  cmSystemTools::Error("GetTargetDirectory"
4745
0
                       " called on cmLocalGenerator");
4746
0
  return "";
4747
0
}
4748
4749
cmPolicies::PolicyStatus cmLocalGenerator::GetPolicyStatus(
4750
  cmPolicies::PolicyID id) const
4751
0
{
4752
0
  return this->Makefile->GetPolicyStatus(id);
4753
0
}
4754
4755
bool cmLocalGenerator::CheckDefinition(std::string const& define) const
4756
0
{
4757
  // Many compilers do not support -DNAME(arg)=sdf so we disable it.
4758
0
  std::string::size_type pos = define.find_first_of("(=");
4759
0
  if (pos != std::string::npos) {
4760
0
    if (define[pos] == '(') {
4761
0
      std::ostringstream e;
4762
      /* clang-format off */
4763
0
      e << "WARNING: Function-style preprocessor definitions may not be "
4764
0
           "passed on the compiler command line because many compilers "
4765
0
           "do not support it.\n"
4766
0
           "CMake is dropping a preprocessor definition: " << define << "\n"
4767
0
           "Consider defining the macro in a (configured) header file.\n";
4768
      /* clang-format on */
4769
0
      cmSystemTools::Message(e.str());
4770
0
      return false;
4771
0
    }
4772
0
  }
4773
4774
  // Many compilers do not support # in the value so we disable it.
4775
0
  if (define.find_first_of('#') != std::string::npos) {
4776
0
    std::ostringstream e;
4777
    /* clang-format off */
4778
0
    e << "WARNING: Preprocessor definitions containing '#' may not be "
4779
0
         "passed on the compiler command line because many compilers "
4780
0
         "do not support it.\n"
4781
0
         "CMake is dropping a preprocessor definition: " << define << "\n"
4782
0
         "Consider defining the macro in a (configured) header file.\n";
4783
    /* clang-format on */
4784
0
    cmSystemTools::Message(e.str());
4785
0
    return false;
4786
0
  }
4787
4788
  // Assume it is supported.
4789
0
  return true;
4790
0
}
4791
4792
static void cmLGInfoProp(cmMakefile* mf, cmGeneratorTarget* target,
4793
                         std::string const& prop)
4794
0
{
4795
0
  if (cmValue val = target->GetProperty(prop)) {
4796
0
    mf->AddDefinition(prop, *val);
4797
0
  }
4798
0
}
4799
4800
void cmLocalGenerator::GenerateAppleInfoPList(cmGeneratorTarget* target,
4801
                                              std::string const& targetName,
4802
                                              std::string const& fname)
4803
0
{
4804
  // Find the Info.plist template.
4805
0
  cmValue in = target->GetProperty("MACOSX_BUNDLE_INFO_PLIST");
4806
0
  std::string inFile = cmNonempty(in) ? *in : "MacOSXBundleInfo.plist.in";
4807
0
  if (!cmSystemTools::FileIsFullPath(inFile)) {
4808
0
    std::string inMod = this->Makefile->GetModulesFile(inFile);
4809
0
    if (!inMod.empty()) {
4810
0
      inFile = inMod;
4811
0
    }
4812
0
  }
4813
0
  if (!cmSystemTools::FileExists(inFile, true)) {
4814
0
    std::ostringstream e;
4815
0
    e << "Target " << target->GetName() << " Info.plist template \"" << inFile
4816
0
      << "\" could not be found.";
4817
0
    cmSystemTools::Error(e.str());
4818
0
    return;
4819
0
  }
4820
4821
  // Convert target properties to variables in an isolated makefile
4822
  // scope to configure the file.  If properties are set they will
4823
  // override user make variables.  If not the configuration will fall
4824
  // back to the directory-level values set by the user.
4825
0
  cmMakefile* mf = this->Makefile;
4826
0
  cmMakefile::ScopePushPop varScope(mf);
4827
0
  mf->AddDefinition("MACOSX_BUNDLE_EXECUTABLE_NAME", targetName);
4828
0
  cmLGInfoProp(mf, target, "MACOSX_BUNDLE_INFO_STRING");
4829
0
  cmLGInfoProp(mf, target, "MACOSX_BUNDLE_ICON_FILE");
4830
0
  cmLGInfoProp(mf, target, "MACOSX_BUNDLE_GUI_IDENTIFIER");
4831
0
  cmLGInfoProp(mf, target, "MACOSX_BUNDLE_LONG_VERSION_STRING");
4832
0
  cmLGInfoProp(mf, target, "MACOSX_BUNDLE_BUNDLE_NAME");
4833
0
  cmLGInfoProp(mf, target, "MACOSX_BUNDLE_SHORT_VERSION_STRING");
4834
0
  cmLGInfoProp(mf, target, "MACOSX_BUNDLE_BUNDLE_VERSION");
4835
0
  cmLGInfoProp(mf, target, "MACOSX_BUNDLE_COPYRIGHT");
4836
0
  mf->ConfigureFile(inFile, fname, false, false, false);
4837
0
}
4838
4839
void cmLocalGenerator::GenerateFrameworkInfoPList(
4840
  cmGeneratorTarget* target, std::string const& targetName,
4841
  std::string const& fname)
4842
0
{
4843
  // Find the Info.plist template.
4844
0
  cmValue in = target->GetProperty("MACOSX_FRAMEWORK_INFO_PLIST");
4845
0
  std::string inFile = cmNonempty(in) ? *in : "MacOSXFrameworkInfo.plist.in";
4846
0
  if (!cmSystemTools::FileIsFullPath(inFile)) {
4847
0
    std::string inMod = this->Makefile->GetModulesFile(inFile);
4848
0
    if (!inMod.empty()) {
4849
0
      inFile = inMod;
4850
0
    }
4851
0
  }
4852
0
  if (!cmSystemTools::FileExists(inFile, true)) {
4853
0
    std::ostringstream e;
4854
0
    e << "Target " << target->GetName() << " Info.plist template \"" << inFile
4855
0
      << "\" could not be found.";
4856
0
    cmSystemTools::Error(e.str());
4857
0
    return;
4858
0
  }
4859
4860
  // Convert target properties to variables in an isolated makefile
4861
  // scope to configure the file.  If properties are set they will
4862
  // override user make variables.  If not the configuration will fall
4863
  // back to the directory-level values set by the user.
4864
0
  cmMakefile* mf = this->Makefile;
4865
0
  cmMakefile::ScopePushPop varScope(mf);
4866
0
  mf->AddDefinition("MACOSX_FRAMEWORK_NAME", targetName);
4867
0
  cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_ICON_FILE");
4868
0
  cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_IDENTIFIER");
4869
0
  cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_SHORT_VERSION_STRING");
4870
0
  cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_BUNDLE_NAME");
4871
0
  cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_BUNDLE_VERSION");
4872
0
  mf->ConfigureFile(inFile, fname, false, false, false);
4873
0
}
4874
4875
namespace {
4876
cm::string_view CustomOutputRoleKeyword(cmLocalGenerator::OutputRole role)
4877
0
{
4878
0
  return (role == cmLocalGenerator::OutputRole::Primary ? "OUTPUT"_s
4879
0
                                                        : "BYPRODUCTS"_s);
4880
0
}
4881
4882
void CreateGeneratedSource(cmLocalGenerator& lg, std::string const& output,
4883
                           cmLocalGenerator::OutputRole role,
4884
                           cmCommandOrigin origin,
4885
                           cmListFileBacktrace const& lfbt)
4886
0
{
4887
0
  if (cmGeneratorExpression::Find(output) != std::string::npos) {
4888
0
    lg.GetCMakeInstance()->IssueMessage(
4889
0
      MessageType::FATAL_ERROR,
4890
0
      "Generator expressions in custom command outputs are not implemented!",
4891
0
      lfbt);
4892
0
    return;
4893
0
  }
4894
4895
  // Make sure the file will not be generated into the source
4896
  // directory during an out of source build.
4897
0
  if (!lg.GetMakefile()->CanIWriteThisFile(output)) {
4898
0
    lg.GetCMakeInstance()->IssueMessage(
4899
0
      MessageType::FATAL_ERROR,
4900
0
      cmStrCat(CustomOutputRoleKeyword(role), " path\n  ", output,
4901
0
               "\nin a source directory as an output of custom command."),
4902
0
      lfbt);
4903
0
    return;
4904
0
  }
4905
4906
  // Make sure the output file name has no invalid characters.
4907
0
  bool const hashNotAllowed = lg.GetState()->UseBorlandMake();
4908
0
  std::string::size_type pos = output.find_first_of("<>");
4909
0
  if (pos == std::string::npos && hashNotAllowed) {
4910
0
    pos = output.find_first_of('#');
4911
0
  }
4912
4913
0
  if (pos != std::string::npos) {
4914
0
    lg.GetCMakeInstance()->IssueMessage(
4915
0
      MessageType::FATAL_ERROR,
4916
0
      cmStrCat(CustomOutputRoleKeyword(role), " containing a \"", output[pos],
4917
0
               "\" is not allowed."),
4918
0
      lfbt);
4919
0
    return;
4920
0
  }
4921
4922
  // Outputs without generator expressions from the project are already
4923
  // created and marked as generated.  Do not mark them again, because
4924
  // other commands might have overwritten the property.
4925
0
  if (origin == cmCommandOrigin::Generator) {
4926
0
    lg.GetMakefile()->GetOrCreateGeneratedSource(output);
4927
0
  }
4928
0
}
4929
4930
std::string ComputeCustomCommandRuleFileName(cmLocalGenerator& lg,
4931
                                             cmListFileBacktrace const& bt,
4932
                                             std::string const& output)
4933
0
{
4934
  // If the output path has no generator expressions, use it directly.
4935
0
  if (cmGeneratorExpression::Find(output) == std::string::npos) {
4936
0
    return output;
4937
0
  }
4938
4939
  // The output path contains a generator expression, but we must choose
4940
  // a single source file path to which to attach the custom command.
4941
  // Use some heuristics to provide a nice-looking name when possible.
4942
4943
  // If the only genex is $<CONFIG>, replace that gracefully.
4944
0
  {
4945
0
    std::string simple = output;
4946
0
    cmSystemTools::ReplaceString(simple, "$<CONFIG>", "(CONFIG)");
4947
0
    if (cmGeneratorExpression::Find(simple) == std::string::npos) {
4948
0
      return simple;
4949
0
    }
4950
0
  }
4951
4952
  // If the genex evaluates to the same value in all configurations, use that.
4953
0
  {
4954
0
    std::vector<std::string> allConfigOutputs =
4955
0
      lg.ExpandCustomCommandOutputGenex(output, bt);
4956
0
    if (allConfigOutputs.size() == 1) {
4957
0
      return allConfigOutputs.front();
4958
0
    }
4959
0
  }
4960
4961
  // Fall back to a deterministic unique name.
4962
0
  cmCryptoHash h(cmCryptoHash::AlgoSHA256);
4963
0
  return cmStrCat(lg.GetCurrentBinaryDirectory(), "/CMakeFiles/",
4964
0
                  h.HashString(output).substr(0, 16));
4965
0
}
4966
4967
cmSourceFile* AddCustomCommand(cmLocalGenerator& lg, cmCommandOrigin origin,
4968
                               std::unique_ptr<cmCustomCommand> cc,
4969
                               bool replace)
4970
0
{
4971
0
  cmMakefile* mf = lg.GetMakefile();
4972
0
  auto const& lfbt = cc->GetBacktrace();
4973
0
  auto const& outputs = cc->GetOutputs();
4974
0
  auto const& byproducts = cc->GetByproducts();
4975
0
  auto const& commandLines = cc->GetCommandLines();
4976
4977
  // Choose a source file on which to store the custom command.
4978
0
  cmSourceFile* file = nullptr;
4979
0
  if (!commandLines.empty() && cc->HasMainDependency()) {
4980
0
    auto const& main_dependency = cc->GetMainDependency();
4981
    // The main dependency was specified.  Use it unless a different
4982
    // custom command already used it.
4983
0
    file = mf->GetSource(main_dependency);
4984
0
    if (file && file->GetCustomCommand() && !replace) {
4985
      // The main dependency already has a custom command.
4986
0
      if (commandLines == file->GetCustomCommand()->GetCommandLines()) {
4987
        // The existing custom command is identical.  Silently ignore
4988
        // the duplicate.
4989
0
        return file;
4990
0
      }
4991
      // The existing custom command is different.  We need to
4992
      // generate a rule file for this new command.
4993
0
      file = nullptr;
4994
0
    } else if (!file) {
4995
0
      file = mf->CreateSource(main_dependency);
4996
0
    }
4997
0
  }
4998
4999
  // Generate a rule file if the main dependency is not available.
5000
0
  if (!file) {
5001
0
    cmGlobalGenerator* gg = lg.GetGlobalGenerator();
5002
5003
    // Construct a rule file associated with the first output produced.
5004
0
    std::string outName = gg->GenerateRuleFile(
5005
0
      ComputeCustomCommandRuleFileName(lg, lfbt, outputs[0]));
5006
5007
    // Check if the rule file already exists.
5008
0
    file = mf->GetSource(outName, cmSourceFileLocationKind::Known);
5009
0
    if (file && file->GetCustomCommand() && !replace) {
5010
      // The rule file already exists.
5011
0
      if (commandLines != file->GetCustomCommand()->GetCommandLines()) {
5012
0
        lg.GetCMakeInstance()->IssueMessage(
5013
0
          MessageType::FATAL_ERROR,
5014
0
          cmStrCat("Attempt to add a custom rule to output\n  ", outName,
5015
0
                   "\nwhich already has a custom rule."),
5016
0
          lfbt);
5017
0
      }
5018
0
      return file;
5019
0
    }
5020
5021
    // Create a cmSourceFile for the rule file.
5022
0
    if (!file) {
5023
0
      file = mf->CreateSource(outName, true, cmSourceFileLocationKind::Known);
5024
0
    }
5025
0
    file->SetProperty("__CMAKE_RULE", "1");
5026
0
  }
5027
5028
  // Attach the custom command to the file.
5029
0
  if (file) {
5030
0
    cc->SetEscapeAllowMakeVars(true);
5031
5032
0
    lg.AddSourceOutputs(file, outputs, cmLocalGenerator::OutputRole::Primary,
5033
0
                        lfbt, origin);
5034
0
    lg.AddSourceOutputs(file, byproducts,
5035
0
                        cmLocalGenerator::OutputRole::Byproduct, lfbt, origin);
5036
5037
0
    file->SetCustomCommand(std::move(cc));
5038
0
  }
5039
0
  return file;
5040
0
}
5041
5042
bool AnyOutputMatches(std::string const& name,
5043
                      std::vector<std::string> const& outputs)
5044
0
{
5045
0
  return std::any_of(outputs.begin(), outputs.end(),
5046
0
                     [&name](std::string const& output) -> bool {
5047
0
                       std::string::size_type pos = output.rfind(name);
5048
                       // If the output matches exactly
5049
0
                       return (pos != std::string::npos &&
5050
0
                               pos == output.size() - name.size() &&
5051
0
                               (pos == 0 || output[pos - 1] == '/'));
5052
0
                     });
5053
0
}
5054
5055
bool AnyTargetCommandOutputMatches(
5056
  std::string const& name, std::vector<cmCustomCommand> const& commands)
5057
0
{
5058
0
  return std::any_of(commands.begin(), commands.end(),
5059
0
                     [&name](cmCustomCommand const& command) -> bool {
5060
0
                       return AnyOutputMatches(name, command.GetByproducts());
5061
0
                     });
5062
0
}
5063
}
5064
5065
namespace detail {
5066
void AddCustomCommandToTarget(cmLocalGenerator& lg, cmCommandOrigin origin,
5067
                              cmTarget* target, cmCustomCommandType type,
5068
                              std::unique_ptr<cmCustomCommand> cc)
5069
0
{
5070
  // Add the command to the appropriate build step for the target.
5071
0
  cc->SetEscapeAllowMakeVars(true);
5072
0
  cc->SetTarget(target->GetName());
5073
5074
0
  lg.AddTargetByproducts(target, cc->GetByproducts(), cc->GetBacktrace(),
5075
0
                         origin);
5076
5077
0
  switch (type) {
5078
0
    case cmCustomCommandType::PRE_BUILD:
5079
0
      target->AddPreBuildCommand(std::move(*cc));
5080
0
      break;
5081
0
    case cmCustomCommandType::PRE_LINK:
5082
0
      target->AddPreLinkCommand(std::move(*cc));
5083
0
      break;
5084
0
    case cmCustomCommandType::POST_BUILD:
5085
0
      target->AddPostBuildCommand(std::move(*cc));
5086
0
      break;
5087
0
  }
5088
5089
0
  cc.reset();
5090
0
}
5091
5092
cmSourceFile* AddCustomCommandToOutput(cmLocalGenerator& lg,
5093
                                       cmCommandOrigin origin,
5094
                                       std::unique_ptr<cmCustomCommand> cc,
5095
                                       bool replace)
5096
0
{
5097
0
  return AddCustomCommand(lg, origin, std::move(cc), replace);
5098
0
}
5099
5100
void AppendCustomCommandToOutput(cmLocalGenerator& lg,
5101
                                 cmListFileBacktrace const& lfbt,
5102
                                 std::string const& output,
5103
                                 std::vector<std::string> const& depends,
5104
                                 cmImplicitDependsList const& implicit_depends,
5105
                                 cmCustomCommandLines const& commandLines)
5106
0
{
5107
  // Lookup an existing command.
5108
0
  cmSourceFile* sf = nullptr;
5109
0
  if (cmGeneratorExpression::Find(output) == std::string::npos) {
5110
0
    sf = lg.GetSourceFileWithOutput(output);
5111
0
  } else {
5112
    // This output path has a generator expression.  Evaluate it to
5113
    // find the output for any configurations.
5114
0
    for (std::string const& out :
5115
0
         lg.ExpandCustomCommandOutputGenex(output, lfbt)) {
5116
0
      sf = lg.GetSourceFileWithOutput(out);
5117
0
      if (sf) {
5118
0
        break;
5119
0
      }
5120
0
    }
5121
0
  }
5122
5123
0
  if (sf) {
5124
0
    if (cmCustomCommand* cc = sf->GetCustomCommand()) {
5125
0
      cc->AppendCommands(commandLines);
5126
0
      cc->AppendDepends(depends);
5127
0
      if (cc->GetCodegen() && !implicit_depends.empty()) {
5128
0
        lg.GetCMakeInstance()->IssueMessage(
5129
0
          MessageType::FATAL_ERROR,
5130
0
          "Cannot append IMPLICIT_DEPENDS to existing CODEGEN custom "
5131
0
          "command.");
5132
0
      }
5133
0
      cc->AppendImplicitDepends(implicit_depends);
5134
0
      return;
5135
0
    }
5136
0
  }
5137
5138
  // No existing command found.
5139
0
  lg.GetCMakeInstance()->IssueMessage(
5140
0
    MessageType::FATAL_ERROR,
5141
0
    cmStrCat("Attempt to APPEND to custom command with output\n  ", output,
5142
0
             "\nwhich is not already a custom command output."),
5143
0
    lfbt);
5144
0
}
5145
5146
void AddUtilityCommand(cmLocalGenerator& lg, cmCommandOrigin origin,
5147
                       cmTarget* target, std::unique_ptr<cmCustomCommand> cc)
5148
0
{
5149
  // They might be moved away
5150
0
  auto byproducts = cc->GetByproducts();
5151
0
  auto lfbt = cc->GetBacktrace();
5152
5153
  // Use an empty comment to avoid generation of default comment.
5154
0
  if (!cc->GetComment()) {
5155
0
    cc->SetComment("");
5156
0
  }
5157
5158
  // Create the generated symbolic output name of the utility target.
5159
0
  std::string output =
5160
0
    lg.CreateUtilityOutput(target->GetName(), byproducts, lfbt);
5161
0
  cc->SetOutputs(output);
5162
5163
0
  cmSourceFile* rule = AddCustomCommand(lg, origin, std::move(cc),
5164
0
                                        /*replace=*/false);
5165
0
  if (rule) {
5166
0
    lg.AddTargetByproducts(target, byproducts, lfbt, origin);
5167
0
  }
5168
5169
0
  target->AddSource(output);
5170
0
}
5171
5172
std::vector<std::string> ComputeISPCObjectSuffixes(cmGeneratorTarget* target)
5173
0
{
5174
0
  cmValue const targetProperty = target->GetProperty("ISPC_INSTRUCTION_SETS");
5175
0
  cmList ispcTargets;
5176
5177
0
  if (!targetProperty.IsOff()) {
5178
0
    ispcTargets.assign(targetProperty);
5179
0
    for (auto& ispcTarget : ispcTargets) {
5180
      // transform targets into the suffixes
5181
0
      auto pos = ispcTarget.find('-');
5182
0
      auto target_suffix = ispcTarget.substr(0, pos);
5183
0
      if (target_suffix ==
5184
0
          "avx1") { // when targeting avx1 ISPC uses the 'avx' output string
5185
0
        target_suffix = "avx";
5186
0
      }
5187
0
      ispcTarget = target_suffix;
5188
0
    }
5189
0
  }
5190
0
  return std::move(ispcTargets.data());
5191
0
}
5192
5193
std::vector<std::string> ComputeISPCExtraObjects(
5194
  std::string const& objectName, std::string const& buildDirectory,
5195
  std::vector<std::string> const& ispcSuffixes)
5196
0
{
5197
0
  auto normalizedDir = cmSystemTools::CollapseFullPath(buildDirectory);
5198
0
  std::vector<std::string> computedObjects;
5199
0
  computedObjects.reserve(ispcSuffixes.size());
5200
5201
0
  auto extension = cmSystemTools::GetFilenameLastExtensionView(objectName);
5202
5203
  // We can't use cmSystemTools::GetFilenameWithoutLastExtension as it
5204
  // drops any directories in objectName
5205
0
  auto objNameNoExt = objectName;
5206
0
  std::string::size_type dot_pos = objectName.rfind('.');
5207
0
  if (dot_pos != std::string::npos) {
5208
0
    objNameNoExt.resize(dot_pos);
5209
0
  }
5210
5211
0
  for (auto const& ispcTarget : ispcSuffixes) {
5212
0
    computedObjects.emplace_back(
5213
0
      cmStrCat(normalizedDir, '/', objNameNoExt, '_', ispcTarget, extension));
5214
0
  }
5215
5216
0
  return computedObjects;
5217
0
}
5218
}
5219
5220
cmSourcesWithOutput cmLocalGenerator::GetSourcesWithOutput(
5221
  std::string const& name) const
5222
0
{
5223
  // Linear search?  Also see GetSourceFileWithOutput for detail.
5224
0
  if (!cmSystemTools::FileIsFullPath(name)) {
5225
0
    cmSourcesWithOutput sources;
5226
0
    sources.Target = this->LinearGetTargetWithOutput(name);
5227
0
    sources.Source = this->LinearGetSourceFileWithOutput(
5228
0
      name, cmSourceOutputKind::OutputOrByproduct, sources.SourceIsByproduct);
5229
0
    return sources;
5230
0
  }
5231
  // Otherwise we use an efficient lookup map.
5232
0
  auto o = this->OutputToSource.find(name);
5233
0
  if (o != this->OutputToSource.end()) {
5234
0
    return o->second.Sources;
5235
0
  }
5236
0
  return {};
5237
0
}
5238
5239
cmSourceFile* cmLocalGenerator::GetSourceFileWithOutput(
5240
  std::string const& name, cmSourceOutputKind kind) const
5241
0
{
5242
  // If the queried path is not absolute we use the backward compatible
5243
  // linear-time search for an output with a matching suffix.
5244
0
  if (!cmSystemTools::FileIsFullPath(name)) {
5245
0
    bool byproduct = false;
5246
0
    return this->LinearGetSourceFileWithOutput(name, kind, byproduct);
5247
0
  }
5248
  // Otherwise we use an efficient lookup map.
5249
0
  auto o = this->OutputToSource.find(name);
5250
0
  if (o != this->OutputToSource.end() &&
5251
0
      (!o->second.Sources.SourceIsByproduct ||
5252
0
       kind == cmSourceOutputKind::OutputOrByproduct)) {
5253
    // Source file could also be null pointer for example if we found the
5254
    // byproduct of a utility target, a PRE_BUILD, PRE_LINK, or POST_BUILD
5255
    // command of a target, or a not yet created custom command.
5256
0
    return o->second.Sources.Source;
5257
0
  }
5258
0
  return nullptr;
5259
0
}
5260
5261
std::string cmLocalGenerator::CreateUtilityOutput(
5262
  std::string const& targetName, std::vector<std::string> const&,
5263
  cmListFileBacktrace const&)
5264
0
{
5265
0
  std::string force =
5266
0
    cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles/", targetName);
5267
  // The output is not actually created so mark it symbolic.
5268
0
  if (cmSourceFile* sf = this->Makefile->GetOrCreateGeneratedSource(force)) {
5269
0
    sf->SetProperty("SYMBOLIC", "1");
5270
0
  } else {
5271
0
    cmSystemTools::Error("Could not get source file entry for " + force);
5272
0
  }
5273
0
  return force;
5274
0
}
5275
5276
std::vector<cmCustomCommandGenerator>
5277
cmLocalGenerator::MakeCustomCommandGenerators(cmCustomCommand const& cc,
5278
                                              std::string const& config)
5279
0
{
5280
0
  std::vector<cmCustomCommandGenerator> ccgs;
5281
0
  ccgs.emplace_back(cc, config, this);
5282
0
  return ccgs;
5283
0
}
5284
5285
std::vector<std::string> cmLocalGenerator::ExpandCustomCommandOutputPaths(
5286
  cmCompiledGeneratorExpression const& cge, std::string const& config)
5287
0
{
5288
0
  cmList paths{ cge.Evaluate(this, config) };
5289
0
  for (std::string& p : paths) {
5290
0
    p = cmSystemTools::CollapseFullPath(p, this->GetCurrentBinaryDirectory());
5291
0
  }
5292
0
  return std::move(paths.data());
5293
0
}
5294
5295
std::vector<std::string> cmLocalGenerator::ExpandCustomCommandOutputGenex(
5296
  std::string const& o, cmListFileBacktrace const& bt)
5297
0
{
5298
0
  std::vector<std::string> allConfigOutputs;
5299
0
  cmGeneratorExpression ge(*this->GetCMakeInstance(), bt);
5300
0
  std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(o);
5301
0
  std::vector<std::string> configs =
5302
0
    this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
5303
0
  for (std::string const& config : configs) {
5304
0
    std::vector<std::string> configOutputs =
5305
0
      this->ExpandCustomCommandOutputPaths(*cge, config);
5306
0
    allConfigOutputs.reserve(allConfigOutputs.size() + configOutputs.size());
5307
0
    std::move(configOutputs.begin(), configOutputs.end(),
5308
0
              std::back_inserter(allConfigOutputs));
5309
0
  }
5310
0
  auto endUnique =
5311
0
    cmRemoveDuplicates(allConfigOutputs.begin(), allConfigOutputs.end());
5312
0
  allConfigOutputs.erase(endUnique, allConfigOutputs.end());
5313
0
  return allConfigOutputs;
5314
0
}
5315
5316
void cmLocalGenerator::AddTargetByproducts(
5317
  cmTarget* target, std::vector<std::string> const& byproducts,
5318
  cmListFileBacktrace const& bt, cmCommandOrigin origin)
5319
0
{
5320
0
  for (std::string const& o : byproducts) {
5321
0
    if (cmGeneratorExpression::Find(o) == std::string::npos) {
5322
0
      this->UpdateOutputToSourceMap(o, target, bt, origin);
5323
0
      continue;
5324
0
    }
5325
5326
    // This byproduct path has a generator expression.  Evaluate it to
5327
    // register the byproducts for all configurations.
5328
0
    for (std::string const& b : this->ExpandCustomCommandOutputGenex(o, bt)) {
5329
0
      this->UpdateOutputToSourceMap(b, target, bt, cmCommandOrigin::Generator);
5330
0
    }
5331
0
  }
5332
0
}
5333
5334
void cmLocalGenerator::AddSourceOutputs(
5335
  cmSourceFile* source, std::vector<std::string> const& outputs,
5336
  OutputRole role, cmListFileBacktrace const& bt, cmCommandOrigin origin)
5337
0
{
5338
0
  for (std::string const& o : outputs) {
5339
0
    if (cmGeneratorExpression::Find(o) == std::string::npos) {
5340
0
      this->UpdateOutputToSourceMap(o, source, role, bt, origin);
5341
0
      continue;
5342
0
    }
5343
5344
    // This output path has a generator expression.  Evaluate it to
5345
    // register the outputs for all configurations.
5346
0
    for (std::string const& out :
5347
0
         this->ExpandCustomCommandOutputGenex(o, bt)) {
5348
0
      this->UpdateOutputToSourceMap(out, source, role, bt,
5349
0
                                    cmCommandOrigin::Generator);
5350
0
    }
5351
0
  }
5352
0
}
5353
5354
void cmLocalGenerator::UpdateOutputToSourceMap(std::string const& byproduct,
5355
                                               cmTarget* target,
5356
                                               cmListFileBacktrace const& bt,
5357
                                               cmCommandOrigin origin)
5358
0
{
5359
0
  SourceEntry entry;
5360
0
  entry.Sources.Target = target;
5361
5362
0
  auto pr = this->OutputToSource.emplace(byproduct, entry);
5363
0
  if (pr.second) {
5364
0
    CreateGeneratedSource(*this, byproduct, OutputRole::Byproduct, origin, bt);
5365
0
  } else {
5366
0
    SourceEntry& current = pr.first->second;
5367
    // Has the target already been set?
5368
0
    if (!current.Sources.Target) {
5369
0
      current.Sources.Target = target;
5370
0
    } else {
5371
      // Multiple custom commands/targets produce the same output (source file
5372
      // or target).  See also comment in other UpdateOutputToSourceMap
5373
      // overload.
5374
      //
5375
      // TODO: Warn the user about this case.
5376
0
    }
5377
0
  }
5378
0
}
5379
5380
void cmLocalGenerator::UpdateOutputToSourceMap(std::string const& output,
5381
                                               cmSourceFile* source,
5382
                                               OutputRole role,
5383
                                               cmListFileBacktrace const& bt,
5384
                                               cmCommandOrigin origin)
5385
0
{
5386
0
  SourceEntry entry;
5387
0
  entry.Sources.Source = source;
5388
0
  entry.Sources.SourceIsByproduct = role == OutputRole::Byproduct;
5389
5390
0
  auto pr = this->OutputToSource.emplace(output, entry);
5391
0
  if (pr.second) {
5392
0
    CreateGeneratedSource(*this, output, role, origin, bt);
5393
0
  } else {
5394
0
    SourceEntry& current = pr.first->second;
5395
    // Outputs take precedence over byproducts
5396
0
    if (!current.Sources.Source ||
5397
0
        (current.Sources.SourceIsByproduct && role == OutputRole::Primary)) {
5398
0
      current.Sources.Source = source;
5399
0
      current.Sources.SourceIsByproduct = false;
5400
0
    } else {
5401
      // Multiple custom commands produce the same output but may
5402
      // be attached to a different source file (MAIN_DEPENDENCY).
5403
      // LinearGetSourceFileWithOutput would return the first one,
5404
      // so keep the mapping for the first one.
5405
      //
5406
      // TODO: Warn the user about this case.  However, the VS 8 generator
5407
      // triggers it for separate generate.stamp rules in ZERO_CHECK and
5408
      // individual targets.
5409
0
    }
5410
0
  }
5411
0
}
5412
5413
cmTarget* cmLocalGenerator::LinearGetTargetWithOutput(
5414
  std::string const& name) const
5415
0
{
5416
  // We go through the ordered vector of targets to get reproducible results
5417
  // should multiple names match.
5418
0
  for (cmTarget* t : this->Makefile->GetOrderedTargets()) {
5419
    // Does the output of any command match the source file name?
5420
0
    if (AnyTargetCommandOutputMatches(name, t->GetPreBuildCommands())) {
5421
0
      return t;
5422
0
    }
5423
0
    if (AnyTargetCommandOutputMatches(name, t->GetPreLinkCommands())) {
5424
0
      return t;
5425
0
    }
5426
0
    if (AnyTargetCommandOutputMatches(name, t->GetPostBuildCommands())) {
5427
0
      return t;
5428
0
    }
5429
0
  }
5430
0
  return nullptr;
5431
0
}
5432
5433
cmSourceFile* cmLocalGenerator::LinearGetSourceFileWithOutput(
5434
  std::string const& name, cmSourceOutputKind kind, bool& byproduct) const
5435
0
{
5436
  // Outputs take precedence over byproducts.
5437
0
  byproduct = false;
5438
0
  cmSourceFile* fallback = nullptr;
5439
5440
  // Look through all the source files that have custom commands and see if the
5441
  // custom command has the passed source file as an output.
5442
0
  for (auto const& src : this->Makefile->GetSourceFiles()) {
5443
    // Does this source file have a custom command?
5444
0
    if (src->GetCustomCommand()) {
5445
      // Does the output of the custom command match the source file name?
5446
0
      if (AnyOutputMatches(name, src->GetCustomCommand()->GetOutputs())) {
5447
        // Return the first matching output.
5448
0
        return src.get();
5449
0
      }
5450
0
      if (kind == cmSourceOutputKind::OutputOrByproduct) {
5451
0
        if (AnyOutputMatches(name, src->GetCustomCommand()->GetByproducts())) {
5452
          // Do not return the source yet as there might be a matching output.
5453
0
          fallback = src.get();
5454
0
        }
5455
0
      }
5456
0
    }
5457
0
  }
5458
5459
  // Did we find a byproduct?
5460
0
  byproduct = fallback != nullptr;
5461
0
  return fallback;
5462
0
}