cmLocalGenerator.cxx 161 KB
Newer Older
1
2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
Ken Martin's avatar
Ken Martin committed
3
#include "cmLocalGenerator.h"
4

5
#include <algorithm>
6
#include <array>
7
8
9
10
11
12
13
14
15
16
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <initializer_list>
#include <iterator>
#include <sstream>
#include <unordered_set>
#include <utility>
#include <vector>

17
#include <cm/memory>
18
#include <cm/string_view>
19
#include <cmext/algorithm>
20
#include <cmext/string_view>
21

22
23
#include "cmsys/RegularExpression.hxx"

24
#include "cmAlgorithms.h"
25
#include "cmComputeLinkInformation.h"
26
#include "cmCustomCommand.h"
27
#include "cmCustomCommandGenerator.h"
28
#include "cmCustomCommandLines.h"
29
#include "cmCustomCommandTypes.h"
30
#include "cmGeneratedFileStream.h"
31
#include "cmGeneratorExpression.h"
32
#include "cmGeneratorExpressionEvaluationFile.h"
33
#include "cmGeneratorTarget.h"
Ken Martin's avatar
updates    
Ken Martin committed
34
#include "cmGlobalGenerator.h"
35
#include "cmInstallGenerator.h"
36
37
#include "cmInstallScriptGenerator.h"
#include "cmInstallTargetGenerator.h"
38
#include "cmLinkLineComputer.h"
Ken Martin's avatar
Ken Martin committed
39
#include "cmMakefile.h"
40
#include "cmRulePlaceholderExpander.h"
41
#include "cmSourceFile.h"
42
#include "cmSourceFileLocation.h"
43
#include "cmSourceFileLocationKind.h"
44
#include "cmStandardLevelResolver.h"
45
46
#include "cmState.h"
#include "cmStateDirectory.h"
47
#include "cmStateTypes.h"
48
#include "cmStringAlgorithms.h"
49
50
#include "cmSystemTools.h"
#include "cmTarget.h"
51
#include "cmTestGenerator.h"
Marc Chevrier's avatar
Marc Chevrier committed
52
#include "cmValue.h"
53
#include "cmVersion.h"
54
55
#include "cmake.h"

56
#if !defined(CMAKE_BOOTSTRAP)
57
58
#  define CM_LG_ENCODE_OBJECT_NAMES
#  include "cmCryptoHash.h"
59
60
#endif

61
#if defined(__HAIKU__)
62
63
#  include <FindDirectory.h>
#  include <StorageDefs.h>
64
65
#endif

66
67
68
69
70
// List of variables that are replaced when
// rules are expanced.  These variables are
// replaced in the form <var> with GetSafeDefinition(var).
// ${LANG} is replaced in the variable first with all enabled
// languages.
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
static auto ruleReplaceVars = { "CMAKE_${LANG}_COMPILER",
                                "CMAKE_SHARED_LIBRARY_CREATE_${LANG}_FLAGS",
                                "CMAKE_SHARED_MODULE_CREATE_${LANG}_FLAGS",
                                "CMAKE_SHARED_MODULE_${LANG}_FLAGS",
                                "CMAKE_SHARED_LIBRARY_${LANG}_FLAGS",
                                "CMAKE_${LANG}_LINK_FLAGS",
                                "CMAKE_SHARED_LIBRARY_SONAME_${LANG}_FLAG",
                                "CMAKE_${LANG}_ARCHIVE",
                                "CMAKE_AR",
                                "CMAKE_CURRENT_SOURCE_DIR",
                                "CMAKE_CURRENT_BINARY_DIR",
                                "CMAKE_RANLIB",
                                "CMAKE_LINKER",
                                "CMAKE_MT",
                                "CMAKE_CUDA_HOST_COMPILER",
                                "CMAKE_CUDA_HOST_LINK_LAUNCHER",
                                "CMAKE_CL_SHOWINCLUDES_PREFIX" };
88

89
90
cmLocalGenerator::cmLocalGenerator(cmGlobalGenerator* gg, cmMakefile* makefile)
  : cmOutputConverter(makefile->GetStateSnapshot())
91
  , DirectoryBacktrace(makefile->GetBacktrace())
Ken Martin's avatar
Ken Martin committed
92
{
93
  this->GlobalGenerator = gg;
94

95
  this->Makefile = makefile;
96

97
98
  this->AliasTargets = makefile->GetAliasTargets();

99
  this->EmitUniversalBinaryFlags = true;
100
101
  this->BackwardsCompatibility = 0;
  this->BackwardsCompatibilityFinal = false;
102
103

  this->ComputeObjectMaxPath();
104

105
106
107
108
109
  // Canonicalize entries of the CPATH environment variable the same
  // way detection of CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES does.
  {
    std::vector<std::string> cpath;
    cmSystemTools::GetPath(cpath, "CPATH");
110
    for (std::string const& cp : cpath) {
111
      if (cmSystemTools::FileIsFullPath(cp)) {
112
        this->EnvCPATH.emplace_back(cmSystemTools::CollapseFullPath(cp));
113
114
115
116
      }
    }
  }

117
118
119
  std::vector<std::string> enabledLanguages =
    this->GetState()->GetEnabledLanguages();

Marc Chevrier's avatar
Marc Chevrier committed
120
  if (cmValue sysrootCompile =
121
        this->Makefile->GetDefinition("CMAKE_SYSROOT_COMPILE")) {
122
    this->CompilerSysroot = *sysrootCompile;
123
124
125
126
  } else {
    this->CompilerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT");
  }

Marc Chevrier's avatar
Marc Chevrier committed
127
  if (cmValue sysrootLink =
128
        this->Makefile->GetDefinition("CMAKE_SYSROOT_LINK")) {
129
    this->LinkerSysroot = *sysrootLink;
130
131
132
  } else {
    this->LinkerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT");
  }
133

Marc Chevrier's avatar
Marc Chevrier committed
134
  if (cmValue appleArchSysroots =
135
        this->Makefile->GetDefinition("CMAKE_APPLE_ARCH_SYSROOTS")) {
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
    std::string const& appleArchs =
      this->Makefile->GetSafeDefinition("CMAKE_OSX_ARCHITECTURES");
    std::vector<std::string> archs;
    std::vector<std::string> sysroots;
    cmExpandList(appleArchs, archs);
    cmExpandList(*appleArchSysroots, sysroots, true);
    if (archs.size() == sysroots.size()) {
      for (size_t i = 0; i < archs.size(); ++i) {
        this->AppleArchSysroots[archs[i]] = sysroots[i];
      }
    } else {
      std::string const e =
        cmStrCat("CMAKE_APPLE_ARCH_SYSROOTS:\n  ", *appleArchSysroots,
                 "\n"
                 "is not the same length as CMAKE_OSX_ARCHITECTURES:\n  ",
                 appleArchs);
      this->IssueMessage(MessageType::FATAL_ERROR, e);
    }
  }

156
  for (std::string const& lang : enabledLanguages) {
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
    if (lang == "NONE") {
      continue;
    }
    this->Compilers["CMAKE_" + lang + "_COMPILER"] = lang;

    this->VariableMappings["CMAKE_" + lang + "_COMPILER"] =
      this->Makefile->GetSafeDefinition("CMAKE_" + lang + "_COMPILER");

    std::string const& compilerArg1 = "CMAKE_" + lang + "_COMPILER_ARG1";
    std::string const& compilerTarget = "CMAKE_" + lang + "_COMPILER_TARGET";
    std::string const& compilerOptionTarget =
      "CMAKE_" + lang + "_COMPILE_OPTIONS_TARGET";
    std::string const& compilerExternalToolchain =
      "CMAKE_" + lang + "_COMPILER_EXTERNAL_TOOLCHAIN";
    std::string const& compilerOptionExternalToolchain =
      "CMAKE_" + lang + "_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN";
    std::string const& compilerOptionSysroot =
      "CMAKE_" + lang + "_COMPILE_OPTIONS_SYSROOT";

    this->VariableMappings[compilerArg1] =
      this->Makefile->GetSafeDefinition(compilerArg1);
    this->VariableMappings[compilerTarget] =
      this->Makefile->GetSafeDefinition(compilerTarget);
    this->VariableMappings[compilerOptionTarget] =
      this->Makefile->GetSafeDefinition(compilerOptionTarget);
    this->VariableMappings[compilerExternalToolchain] =
      this->Makefile->GetSafeDefinition(compilerExternalToolchain);
    this->VariableMappings[compilerOptionExternalToolchain] =
      this->Makefile->GetSafeDefinition(compilerOptionExternalToolchain);
    this->VariableMappings[compilerOptionSysroot] =
      this->Makefile->GetSafeDefinition(compilerOptionSysroot);

189
190
191
    for (std::string replaceVar : ruleReplaceVars) {
      if (replaceVar.find("${LANG}") != std::string::npos) {
        cmSystemTools::ReplaceString(replaceVar, "${LANG}", lang);
192
193
      }

194
195
      this->VariableMappings[replaceVar] =
        this->Makefile->GetSafeDefinition(replaceVar);
196
197
    }
  }
Ken Martin's avatar
Ken Martin committed
198
199
}

200
201
202
203
cmRulePlaceholderExpander* cmLocalGenerator::CreateRulePlaceholderExpander()
  const
{
  return new cmRulePlaceholderExpander(this->Compilers, this->VariableMappings,
204
205
                                       this->CompilerSysroot,
                                       this->LinkerSysroot);
206
207
}

208
cmLocalGenerator::~cmLocalGenerator() = default;
Ken Martin's avatar
Ken Martin committed
209

210
void cmLocalGenerator::IssueMessage(MessageType t,
211
212
                                    std::string const& text) const
{
213
  this->GetCMakeInstance()->IssueMessage(t, text, this->DirectoryBacktrace);
214
215
}

216
217
void cmLocalGenerator::ComputeObjectMaxPath()
{
218
// Choose a maximum object file name length.
219
220
221
222
223
#if defined(_WIN32) || defined(__CYGWIN__)
  this->ObjectPathMax = 250;
#else
  this->ObjectPathMax = 1000;
#endif
Marc Chevrier's avatar
Marc Chevrier committed
224
  cmValue plen = this->Makefile->GetDefinition("CMAKE_OBJECT_PATH_MAX");
Vitaly Stakhovsky's avatar
Vitaly Stakhovsky committed
225
  if (cmNonempty(plen)) {
226
    unsigned int pmax;
227
    if (sscanf(plen->c_str(), "%u", &pmax) == 1) {
228
      if (pmax >= 128) {
229
        this->ObjectPathMax = pmax;
230
      } else {
231
        std::ostringstream w;
232
233
234
        w << "CMAKE_OBJECT_PATH_MAX is set to " << pmax
          << ", which is less than the minimum of 128.  "
          << "The value will be ignored.";
235
        this->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
236
      }
237
    } else {
238
      std::ostringstream w;
239
      w << "CMAKE_OBJECT_PATH_MAX is set to \"" << *plen
240
241
        << "\", which fails to parse as a positive integer.  "
        << "The value will be ignored.";
242
      this->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
243
    }
244
  }
245
  this->ObjectMaxPathViolations.clear();
246
247
}

248
249
250
251
static void MoveSystemIncludesToEnd(std::vector<std::string>& includeDirs,
                                    const std::string& config,
                                    const std::string& lang,
                                    const cmGeneratorTarget* target)
252
253
254
255
256
257
258
259
260
261
262
263
264
{
  if (!target) {
    return;
  }

  std::stable_sort(
    includeDirs.begin(), includeDirs.end(),
    [&target, &config, &lang](std::string const& a, std::string const& b) {
      return !target->IsSystemIncludeDirectory(a, config, lang) &&
        target->IsSystemIncludeDirectory(b, config, lang);
    });
}

265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
static void MoveSystemIncludesToEnd(std::vector<BT<std::string>>& includeDirs,
                                    const std::string& config,
                                    const std::string& lang,
                                    const cmGeneratorTarget* target)
{
  if (!target) {
    return;
  }

  std::stable_sort(includeDirs.begin(), includeDirs.end(),
                   [target, &config, &lang](BT<std::string> const& a,
                                            BT<std::string> const& b) {
                     return !target->IsSystemIncludeDirectory(a.Value, config,
                                                              lang) &&
                       target->IsSystemIncludeDirectory(b.Value, config, lang);
                   });
}

283
void cmLocalGenerator::TraceDependencies() const
Alexander Neundorf's avatar
   
Alexander Neundorf committed
284
285
{
  // Generate the rule files for each target.
286
287
  const auto& targets = this->GetGeneratorTargets();
  for (const auto& target : targets) {
288
    if (!target->IsInBuildSystem()) {
289
      continue;
Alexander Neundorf's avatar
   
Alexander Neundorf committed
290
    }
291
    target->TraceDependencies();
292
  }
Alexander Neundorf's avatar
   
Alexander Neundorf committed
293
294
}

295
296
void cmLocalGenerator::GenerateTestFiles()
{
297
  if (!this->Makefile->IsOn("CMAKE_TESTING_ENABLED")) {
298
    return;
299
  }
300
301

  // Compute the set of configurations.
302
303
304
  std::vector<std::string> configurationTypes =
    this->Makefile->GetGeneratorConfigs(cmMakefile::OnlyMultiConfig);
  std::string config = this->Makefile->GetDefaultConfiguration();
305

306
307
308
  std::string file =
    cmStrCat(this->StateSnapshot.GetDirectory().GetCurrentBinary(),
             "/CTestTestfile.cmake");
309

310
  cmGeneratedFileStream fout(file);
311
312
  fout.SetCopyIfDifferent(true);

313
314
315
316
317
318
319
320
321
322
323
324
  fout << "# CMake generated Testfile for \n"
          "# Source directory: "
       << this->StateSnapshot.GetDirectory().GetCurrentSource()
       << "\n"
          "# Build directory: "
       << this->StateSnapshot.GetDirectory().GetCurrentBinary()
       << "\n"
          "# \n"
          "# This file includes the relevant testing commands "
          "required for \n"
          "# testing this directory and lists subdirectories to "
          "be tested as well.\n";
325

326
327
328
329
330
331
  std::string resourceSpecFile =
    this->Makefile->GetSafeDefinition("CTEST_RESOURCE_SPEC_FILE");
  if (!resourceSpecFile.empty()) {
    fout << "set(CTEST_RESOURCE_SPEC_FILE \"" << resourceSpecFile << "\")\n";
  }

Marc Chevrier's avatar
Marc Chevrier committed
332
  cmValue testIncludeFile = this->Makefile->GetProperty("TEST_INCLUDE_FILE");
333
  if (testIncludeFile) {
334
    fout << "include(\"" << *testIncludeFile << "\")\n";
335
  }
336

Marc Chevrier's avatar
Marc Chevrier committed
337
  cmValue testIncludeFiles = this->Makefile->GetProperty("TEST_INCLUDE_FILES");
Matthew Woehlke's avatar
Matthew Woehlke committed
338
  if (testIncludeFiles) {
339
    std::vector<std::string> includesList = cmExpandedList(*testIncludeFiles);
340
    for (std::string const& i : includesList) {
341
      fout << "include(\"" << i << "\")\n";
Matthew Woehlke's avatar
Matthew Woehlke committed
342
343
344
    }
  }

345
  // Ask each test generator to write its code.
346
  for (const auto& tester : this->Makefile->GetTestGenerators()) {
347
348
    tester->Compute(this);
    tester->Generate(fout, config, configurationTypes);
349
  }
350
  using vec_t = std::vector<cmStateSnapshot>;
351
  vec_t const& children = this->Makefile->GetStateSnapshot().GetChildren();
352
  for (cmStateSnapshot const& i : children) {
353
    // TODO: Use add_subdirectory instead?
354
    std::string outP = i.GetDirectory().GetCurrentBinary();
355
    outP = this->MaybeRelativeToCurBinDir(outP);
356
    outP = cmOutputConverter::EscapeForCMake(outP);
357
    fout << "subdirs(" << outP << ")\n";
358
  }
359
360

  // Add directory labels property
Marc Chevrier's avatar
Marc Chevrier committed
361
  cmValue directoryLabels =
362
    this->Makefile->GetDefinition("CMAKE_DIRECTORY_LABELS");
Marc Chevrier's avatar
Marc Chevrier committed
363
  cmValue labels = this->Makefile->GetProperty("LABELS");
364
365
366
367

  if (labels || directoryLabels) {
    fout << "set_directory_properties(PROPERTIES LABELS ";
    if (labels) {
368
      fout << cmOutputConverter::EscapeForCMake(*labels);
369
370
371
372
373
    }
    if (labels && directoryLabels) {
      fout << ";";
    }
    if (directoryLabels) {
374
      fout << cmOutputConverter::EscapeForCMake(*directoryLabels);
375
    }
376
    fout << ")\n";
377
  }
378
379
}

380
381
382
void cmLocalGenerator::CreateEvaluationFileOutputs()
{
  std::vector<std::string> const& configs =
383
    this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
384
385
386
387
388
  for (std::string const& c : configs) {
    this->CreateEvaluationFileOutputs(c);
  }
}

389
390
void cmLocalGenerator::CreateEvaluationFileOutputs(std::string const& config)
{
391
  for (const auto& geef : this->Makefile->GetEvaluationFiles()) {
392
    geef->CreateOutputFile(this, config);
393
  }
394
395
396
}

void cmLocalGenerator::ProcessEvaluationFiles(
397
  std::vector<std::string>& generatedFiles)
398
{
399
  for (const auto& geef : this->Makefile->GetEvaluationFiles()) {
400
    geef->Generate(this);
401
    if (cmSystemTools::GetFatalErrorOccured()) {
402
      return;
403
    }
404
    std::vector<std::string> files = geef->GetFiles();
405
406
407
    std::sort(files.begin(), files.end());

    std::vector<std::string> intersection;
408
409
    std::set_intersection(files.begin(), files.end(), generatedFiles.begin(),
                          generatedFiles.end(),
410
                          std::back_inserter(intersection));
411
    if (!intersection.empty()) {
412
      cmSystemTools::Error("Files to be generated by multiple different "
413
414
                           "commands: " +
                           cmWrap('"', intersection, '"', " "));
415
      return;
416
    }
417

418
    cm::append(generatedFiles, files);
419
420
421
    std::inplace_merge(generatedFiles.begin(),
                       generatedFiles.end() - files.size(),
                       generatedFiles.end());
422
  }
423
424
}

425
426
void cmLocalGenerator::GenerateInstallRules()
{
427
  // Compute the install prefix.
Marc Chevrier's avatar
Marc Chevrier committed
428
429
  cmValue installPrefix =
    this->Makefile->GetDefinition("CMAKE_INSTALL_PREFIX");
430
  std::string prefix = installPrefix;
431

432
#if defined(_WIN32) && !defined(__CYGWIN__)
433
434
435
  if (!installPrefix) {
    if (!cmSystemTools::GetEnv("SystemDrive", prefix)) {
      prefix = "C:";
436
    }
Marc Chevrier's avatar
Marc Chevrier committed
437
    cmValue project_name = this->Makefile->GetDefinition("PROJECT_NAME");
Vitaly Stakhovsky's avatar
Vitaly Stakhovsky committed
438
    if (cmNonempty(project_name)) {
439
440
      prefix += "/Program Files/";
      prefix += *project_name;
441
    } else {
442
      prefix += "/InstalledCMakeProject";
443
    }
444
  }
445
#elif defined(__HAIKU__)
446
  char dir[B_PATH_NAME_LENGTH];
447
  if (!installPrefix) {
448
449
    if (find_directory(B_SYSTEM_DIRECTORY, -1, false, dir, sizeof(dir)) ==
        B_OK) {
450
      prefix = dir;
451
    } else {
452
      prefix = "/boot/system";
453
    }
454
  }
455
#else
456
  if (!installPrefix) {
457
    prefix = "/usr/local";
458
  }
459
#endif
Marc Chevrier's avatar
Marc Chevrier committed
460
  if (cmValue stagingPrefix =
461
        this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX")) {
462
    prefix = *stagingPrefix;
463
  }
464

465
  // Compute the set of configurations.
466
467
468
  std::vector<std::string> configurationTypes =
    this->Makefile->GetGeneratorConfigs(cmMakefile::OnlyMultiConfig);
  std::string config = this->Makefile->GetDefaultConfiguration();
469

470
  // Choose a default install configuration.
471
  std::string default_config = config;
472
  const char* default_order[] = { "RELEASE", "MINSIZEREL", "RELWITHDEBINFO",
Daniel Pfeifer's avatar
Daniel Pfeifer committed
473
                                  "DEBUG", nullptr };
474
  for (const char** c = default_order; *c && default_config.empty(); ++c) {
475
476
477
    for (std::string const& configurationType : configurationTypes) {
      if (cmSystemTools::UpperCase(configurationType) == *c) {
        default_config = configurationType;
478
479
      }
    }
480
481
  }
  if (default_config.empty() && !configurationTypes.empty()) {
482
    default_config = configurationTypes[0];
483
  }
484

485
  // Create the install script file.
486
  std::string file = this->StateSnapshot.GetDirectory().GetCurrentBinary();
487
  std::string homedir = this->GetState()->GetBinaryDirectory();
488
  int toplevel_install = 0;
489
  if (file == homedir) {
490
    toplevel_install = 1;
491
  }
492
  file += "/cmake_install.cmake";
493
  cmGeneratedFileStream fout(file);
494
  fout.SetCopyIfDifferent(true);
495

496
  // Write the header.
497
  /* clang-format off */
498
  fout << "# Install script for directory: "
499
500
501
502
503
504
       << this->StateSnapshot.GetDirectory().GetCurrentSource()
       << "\n\n"
          "# Set the install prefix\n"
          "if(NOT DEFINED CMAKE_INSTALL_PREFIX)\n"
          "  set(CMAKE_INSTALL_PREFIX \"" << prefix << "\")\n"
          "endif()\n"
505
       << R"(string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX )"
506
507
       << "\"${CMAKE_INSTALL_PREFIX}\")\n\n";
  /* clang-format on */
508

509
  // Write support code for generating per-configuration install rules.
510
  /* clang-format off */
511
512
  fout <<
    "# Set the install configuration name.\n"
513
514
515
    "if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)\n"
    "  if(BUILD_TYPE)\n"
    "    string(REGEX REPLACE \"^[^A-Za-z0-9_]+\" \"\"\n"
516
    "           CMAKE_INSTALL_CONFIG_NAME \"${BUILD_TYPE}\")\n"
517
518
519
520
    "  else()\n"
    "    set(CMAKE_INSTALL_CONFIG_NAME \"" << default_config << "\")\n"
    "  endif()\n"
    "  message(STATUS \"Install configuration: "
Ken Martin's avatar
Ken Martin committed
521
    "\\\"${CMAKE_INSTALL_CONFIG_NAME}\\\"\")\n"
522
    "endif()\n"
523
    "\n";
524
  /* clang-format on */
525

526
  // Write support code for dealing with component-specific installs.
527
  /* clang-format off */
528
529
  fout <<
    "# Set the component getting installed.\n"
530
531
532
533
534
535
536
537
    "if(NOT CMAKE_INSTALL_COMPONENT)\n"
    "  if(COMPONENT)\n"
    "    message(STATUS \"Install component: \\\"${COMPONENT}\\\"\")\n"
    "    set(CMAKE_INSTALL_COMPONENT \"${COMPONENT}\")\n"
    "  else()\n"
    "    set(CMAKE_INSTALL_COMPONENT)\n"
    "  endif()\n"
    "endif()\n"
538
    "\n";
539
  /* clang-format on */
540

541
  // Copy user-specified install options to the install code.
Marc Chevrier's avatar
Marc Chevrier committed
542
  if (cmValue so_no_exe =
543
        this->Makefile->GetDefinition("CMAKE_INSTALL_SO_NO_EXE")) {
544
    /* clang-format off */
545
546
    fout <<
      "# Install shared libraries without execute permission?\n"
547
      "if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)\n"
548
      "  set(CMAKE_INSTALL_SO_NO_EXE \"" << *so_no_exe << "\")\n"
549
      "endif()\n"
550
      "\n";
551
    /* clang-format on */
552
  }
553

554
  // Copy cmake cross compile state to install code.
Marc Chevrier's avatar
Marc Chevrier committed
555
  if (cmValue crosscompiling =
556
557
558
559
560
        this->Makefile->GetDefinition("CMAKE_CROSSCOMPILING")) {
    /* clang-format off */
    fout <<
      "# Is this installation the result of a crosscompile?\n"
      "if(NOT DEFINED CMAKE_CROSSCOMPILING)\n"
561
      "  set(CMAKE_CROSSCOMPILING \"" << *crosscompiling << "\")\n"
562
563
564
565
566
      "endif()\n"
      "\n";
    /* clang-format on */
  }

567
  // Write default directory permissions.
Marc Chevrier's avatar
Marc Chevrier committed
568
  if (cmValue defaultDirPermissions = this->Makefile->GetDefinition(
569
570
571
572
573
574
        "CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS")) {
    /* clang-format off */
    fout <<
      "# Set default install directory permissions.\n"
      "if(NOT DEFINED CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS)\n"
      "  set(CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS \""
575
         << *defaultDirPermissions << "\")\n"
576
577
578
579
580
      "endif()\n"
      "\n";
    /* clang-format on */
  }

581
582
583
  // Write out CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM so that
  // installed code that uses `file(GET_RUNTIME_DEPENDENCIES)`
  // has same platform variable as when running cmake
Marc Chevrier's avatar
Marc Chevrier committed
584
  if (cmValue platform = this->Makefile->GetDefinition(
585
586
587
588
589
590
        "CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM")) {
    /* clang-format off */
    fout <<
      "# Set default install directory permissions.\n"
      "if(NOT DEFINED CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM)\n"
      "  set(CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM \""
591
         << *platform << "\")\n"
592
593
594
595
596
597
598
599
      "endif()\n"
      "\n";
    /* clang-format on */
  }

  // Write out CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL so that
  // installed code that uses `file(GET_RUNTIME_DEPENDENCIES)`
  // has same tool selected as when running cmake
Marc Chevrier's avatar
Marc Chevrier committed
600
  if (cmValue command =
601
602
603
604
605
606
        this->Makefile->GetDefinition("CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL")) {
    /* clang-format off */
    fout <<
      "# Set default install directory permissions.\n"
      "if(NOT DEFINED CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL)\n"
      "  set(CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL \""
607
         << *command << "\")\n"
608
609
610
611
612
613
614
615
      "endif()\n"
      "\n";
    /* clang-format on */
  }

  // Write out CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND so that
  // installed code that uses `file(GET_RUNTIME_DEPENDENCIES)`
  // has same path to the tool as when running cmake
Marc Chevrier's avatar
Marc Chevrier committed
616
  if (cmValue command = this->Makefile->GetDefinition(
617
618
619
620
621
622
        "CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND")) {
    /* clang-format off */
    fout <<
      "# Set default install directory permissions.\n"
      "if(NOT DEFINED CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND)\n"
      "  set(CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND \""
623
         << *command << "\")\n"
624
625
626
627
628
629
630
631
632
633
      "endif()\n"
      "\n";
    /* clang-format on */
  }

  // Write out CMAKE_OBJDUMP so that installed code that uses
  // `file(GET_RUNTIME_DEPENDENCIES)` and hasn't specified
  // CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND has consistent
  // logic to fallback to CMAKE_OBJDUMP when `objdump` is
  // not on the path
Marc Chevrier's avatar
Marc Chevrier committed
634
  if (cmValue command = this->Makefile->GetDefinition("CMAKE_OBJDUMP")) {
635
636
637
638
639
    /* clang-format off */
    fout <<
      "# Set default install directory permissions.\n"
      "if(NOT DEFINED CMAKE_OBJDUMP)\n"
      "  set(CMAKE_OBJDUMP \""
640
         << *command << "\")\n"
641
642
643
644
645
      "endif()\n"
      "\n";
    /* clang-format on */
  }

646
647
  this->AddGeneratorSpecificInstallSetup(fout);

648
  // Ask each install generator to write its code.
649
  cmPolicies::PolicyStatus status = this->GetPolicyStatus(cmPolicies::CMP0082);
650
  auto const& installers = this->Makefile->GetInstallGenerators();
651
652
653
  bool haveSubdirectoryInstall = false;
  bool haveInstallAfterSubdirectory = false;
  if (status == cmPolicies::WARN) {
654
    for (const auto& installer : installers) {
655
656
657
658
659
      installer->CheckCMP0082(haveSubdirectoryInstall,
                              haveInstallAfterSubdirectory);
      installer->Generate(fout, config, configurationTypes);
    }
  } else {
660
    for (const auto& installer : installers) {
661
662
      installer->Generate(fout, config, configurationTypes);
    }
663
  }
664

665
666
  // Write rules from old-style specification stored in targets.
  this->GenerateTargetInstallRules(fout, config, configurationTypes);
667

668
  // Include install scripts from subdirectories.
669
670
671
672
673
674
675
  switch (status) {
    case cmPolicies::WARN:
      if (haveInstallAfterSubdirectory &&
          this->Makefile->PolicyOptionalWarningEnabled(
            "CMAKE_POLICY_WARNING_CMP0082")) {
        std::ostringstream e;
        e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0082) << "\n";
676
        this->IssueMessage(MessageType::AUTHOR_WARNING, e.str());
677
      }
678
679
680
681
682
683
684
685
686
687
688
      CM_FALLTHROUGH;
    case cmPolicies::OLD: {
      std::vector<cmStateSnapshot> children =
        this->Makefile->GetStateSnapshot().GetChildren();
      if (!children.empty()) {
        fout << "if(NOT CMAKE_INSTALL_LOCAL_ONLY)\n";
        fout << "  # Include the install script for each subdirectory.\n";
        for (cmStateSnapshot const& c : children) {
          if (!c.GetDirectory().GetPropertyAsBool("EXCLUDE_FROM_ALL")) {
            std::string odir = c.GetDirectory().GetCurrentBinary();
            cmSystemTools::ConvertToUnixSlashes(odir);
689
            fout << "  include(\"" << odir << "/cmake_install.cmake\")\n";
690
691
692
693
694
695
696
697
698
699
700
701
702
          }
        }
        fout << "\n";
        fout << "endif()\n\n";
      }
    } break;

    case cmPolicies::REQUIRED_IF_USED:
    case cmPolicies::REQUIRED_ALWAYS:
    case cmPolicies::NEW:
      // NEW behavior is handled in
      // cmInstallSubdirectoryGenerator::GenerateScript()
      break;
703
  }
704
705

  // Record the install manifest.
706
  if (toplevel_install) {
707
    /* clang-format off */
708
    fout <<
709
710
      "if(CMAKE_INSTALL_COMPONENT)\n"
      "  set(CMAKE_INSTALL_MANIFEST \"install_manifest_"
711
      "${CMAKE_INSTALL_COMPONENT}.txt\")\n"
712
713
      "else()\n"
      "  set(CMAKE_INSTALL_MANIFEST \"install_manifest.txt\")\n"
714
715
716
717
718
719
      "endif()\n"
      "\n"
      "string(REPLACE \";\" \"\\n\" CMAKE_INSTALL_MANIFEST_CONTENT\n"
      "       \"${CMAKE_INSTALL_MANIFEST_FILES}\")\n"
      "file(WRITE \"" << homedir << "/${CMAKE_INSTALL_MANIFEST}\"\n"
      "     \"${CMAKE_INSTALL_MANIFEST_CONTENT}\")\n";
720
    /* clang-format on */
721
  }
722
723
}

724
725
void cmLocalGenerator::AddGeneratorTarget(
  std::unique_ptr<cmGeneratorTarget> gt)
726
{
727
728
729
730
731
  cmGeneratorTarget* gt_ptr = gt.get();

  this->GeneratorTargets.push_back(std::move(gt));
  this->GeneratorTargetSearchIndex.emplace(gt_ptr->GetName(), gt_ptr);
  this->GlobalGenerator->IndexGeneratorTarget(gt_ptr);
732
733
}

734
735
void cmLocalGenerator::AddImportedGeneratorTarget(cmGeneratorTarget* gt)
{
736
  this->ImportedGeneratorTargets.emplace(gt->GetName(), gt);
737
  this->GlobalGenerator->IndexGeneratorTarget(gt);
738
739
}

740
741
void cmLocalGenerator::AddOwnedImportedGeneratorTarget(
  std::unique_ptr<cmGeneratorTarget> gt)
742
{
743
  this->OwnedImportedGeneratorTargets.push_back(std::move(gt));
744
745
}

746
cmGeneratorTarget* cmLocalGenerator::FindLocalNonAliasGeneratorTarget(
747
  const std::string& name) const
748
{
wahikihiki's avatar
wahikihiki committed
749
  auto ti = this->GeneratorTargetSearchIndex.find(name);
750
751
  if (ti != this->GeneratorTargetSearchIndex.end()) {
    return ti->second;
752
  }
Daniel Pfeifer's avatar
Daniel Pfeifer committed
753
  return nullptr;
754
755
}

756
void cmLocalGenerator::ComputeTargetManifest()
757
758
{
  // Collect the set of configuration types.
759
760
  std::vector<std::string> configNames =
    this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
761
762

  // Add our targets to the manifest for each configuration.
763
764
  const auto& targets = this->GetGeneratorTargets();
  for (const auto& target : targets) {
765
    if (!target->IsInBuildSystem()) {
766
      continue;
767
    }
768
769
    for (std::string const& c : configNames) {
      target->ComputeTargetManifest(c);
770
    }
771
  }
772
773
}

774
775
776
bool cmLocalGenerator::ComputeTargetCompileFeatures()
{
  // Collect the set of configuration types.
777
778
  std::vector<std::string> configNames =
    this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
779

780
  using LanguagePair = std::pair<std::string, std::string>;
Robert Maynard's avatar
Robert Maynard committed
781
782
783
  std::vector<LanguagePair> pairedLanguages{
    { "OBJC", "C" }, { "OBJCXX", "CXX" }, { "CUDA", "CXX" }, { "HIP", "CXX" }
  };
784
  std::set<LanguagePair> inferredEnabledLanguages;
785
786
  for (auto const& lang : pairedLanguages) {
    if (this->Makefile->GetState()->GetLanguageEnabled(lang.first)) {
787
      inferredEnabledLanguages.insert(lang);
788
789
790
    }
  }

791
  // Process compile features of all targets.
792
793
  const auto& targets = this->GetGeneratorTargets();
  for (const auto& target : targets) {
794
795
    for (std::string const& c : configNames) {
      if (!target->ComputeCompileFeatures(c)) {
796
797
798
        return false;
      }
    }
799
800
801

    // Now that C/C++ _STANDARD values have been computed
    // set the values to ObjC/ObjCXX _STANDARD variables
802
    if (target->CanCompileSources()) {
803
804
      for (std::string const& c : configNames) {
        target->ComputeCompileFeatures(c, inferredEnabledLanguages);
805
      }
806
    }
807
808
809
810
811
  }

  return true;
}

812
813
814
815
816
bool cmLocalGenerator::IsRootMakefile() const
{
  return !this->StateSnapshot.GetBuildsystemDirectoryParent().IsValid();
}

817
818
819
820
821
cmState* cmLocalGenerator::GetState() const
{
  return this->GlobalGenerator->GetCMakeInstance()->GetState();
}

822
cmStateSnapshot cmLocalGenerator::GetStateSnapshot() const
823
{
824
  return this->Makefile->GetStateSnapshot();
825
826
}

Marc Chevrier's avatar
Marc Chevrier committed
827
828
cmValue cmLocalGenerator::GetRuleLauncher(cmGeneratorTarget* target,
                                          const std::string& prop)
829
{
830
  if (target) {
831
    return target->GetProperty(prop);
832
  }
833
  return this->Makefile->GetProperty(prop);
834
835
}

836
std::string cmLocalGenerator::ConvertToIncludeReference(
837
  std::string const& path, IncludePathStyle pathStyle, OutputFormat format)
838
{
839
  static_cast<void>(pathStyle);
840
  return this->ConvertToOutputForExisting(path, format);
841
842
}

843
std::string cmLocalGenerator::GetIncludeFlags(
844
845
846
  std::vector<std::string> const& includeDirs, cmGeneratorTarget* target,
  std::string const& lang, std::string const& config, bool forResponseFile,
  IncludePathStyle pathStyle)
847
848
{
  if (lang.empty()) {
849
    return "";
850
  }
851

852
  std::vector<std::string> includes = includeDirs;
853
  MoveSystemIncludesToEnd(includes, config, lang, target);
854

855
  OutputFormat shellFormat = forResponseFile ? RESPONSE : SHELL;
856
  std::ostringstream includeFlags;
857

wahikihiki's avatar
wahikihiki committed
858
859
  std::string const& includeFlag =
    this->Makefile->GetSafeDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_", lang));
860
  bool quotePaths = false;
861
  if (this->Makefile->GetDefinition("CMAKE_QUOTE_INCLUDE_PATHS")) {
862
    quotePaths = true;
863
  }
Marc Chevrier's avatar
Marc Chevrier committed
864
  std::string sep = " ";
865
  bool repeatFlag = true;