cmLocalGenerator.cxx 157 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 "cmRange.h"
41
#include "cmRulePlaceholderExpander.h"
42
#include "cmSourceFile.h"
43
#include "cmSourceFileLocation.h"
44
#include "cmSourceFileLocationKind.h"
45
#include "cmStandardLevelResolver.h"
46
47
#include "cmState.h"
#include "cmStateDirectory.h"
48
#include "cmStateTypes.h"
49
#include "cmStringAlgorithms.h"
50
51
#include "cmSystemTools.h"
#include "cmTarget.h"
52
#include "cmTestGenerator.h"
Marc Chevrier's avatar
Marc Chevrier committed
53
#include "cmValue.h"
54
#include "cmVersion.h"
55
56
#include "cmake.h"

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

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

67
68
69
70
71
// 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.
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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" };
89

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

96
  this->Makefile = makefile;
97

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

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

  this->ComputeObjectMaxPath();
105

106
107
108
109
110
  // 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");
111
    for (std::string const& cp : cpath) {
112
      if (cmSystemTools::FileIsFullPath(cp)) {
113
        this->EnvCPATH.emplace_back(cmSystemTools::CollapseFullPath(cp));
114
115
116
117
      }
    }
  }

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

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

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

Marc Chevrier's avatar
Marc Chevrier committed
135
  if (cmValue appleArchSysroots =
136
        this->Makefile->GetDefinition("CMAKE_APPLE_ARCH_SYSROOTS")) {
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
    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);
    }
  }

157
  for (std::string const& lang : enabledLanguages) {
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
189
    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);

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

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

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

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

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

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

249
250
251
252
static void MoveSystemIncludesToEnd(std::vector<std::string>& includeDirs,
                                    const std::string& config,
                                    const std::string& lang,
                                    const cmGeneratorTarget* target)
253
254
255
256
257
258
259
260
261
262
263
264
265
{
  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);
    });
}

266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
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);
                   });
}

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

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

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

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

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

314
315
316
317
318
319
320
321
322
323
324
325
  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";
326

327
328
329
330
331
332
  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
333
  cmValue testIncludeFile = this->Makefile->GetProperty("TEST_INCLUDE_FILE");
334
  if (testIncludeFile) {
335
    fout << "include(\"" << *testIncludeFile << "\")\n";
336
  }
337

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

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

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

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

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

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

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

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

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

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

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

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

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

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

497
  // Write the header.
498
  /* clang-format off */
499
  fout << "# Install script for directory: "
500
501
502
503
504
505
       << 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"
506
       << R"(string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX )"
507
508
       << "\"${CMAKE_INSTALL_PREFIX}\")\n\n";
  /* clang-format on */
509

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

527
  // Write support code for dealing with component-specific installs.
528
  /* clang-format off */
529
530
  fout <<
    "# Set the component getting installed.\n"
531
532
533
534
535
536
537
538
    "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"
539
    "\n";
540
  /* clang-format on */
541

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

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

568
  // Write default directory permissions.
Marc Chevrier's avatar
Marc Chevrier committed
569
  if (cmValue defaultDirPermissions = this->Makefile->GetDefinition(
570
571
572
573
574
575
        "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 \""
576
         << *defaultDirPermissions << "\")\n"
577
578
579
580
581
      "endif()\n"
      "\n";
    /* clang-format on */
  }

582
583
584
  // 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
585
  if (cmValue platform = this->Makefile->GetDefinition(
586
587
588
589
590
591
        "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 \""
592
         << *platform << "\")\n"
593
594
595
596
597
598
599
600
      "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
601
  if (cmValue command =
602
603
604
605
606
607
        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 \""
608
         << *command << "\")\n"
609
610
611
612
613
614
615
616
      "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
617
  if (cmValue command = this->Makefile->GetDefinition(
618
619
620
621
622
623
        "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 \""
624
         << *command << "\")\n"
625
626
627
628
629
630
631
632
633
634
      "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
635
  if (cmValue command = this->Makefile->GetDefinition("CMAKE_OBJDUMP")) {
636
637
638
639
640
    /* clang-format off */
    fout <<
      "# Set default install directory permissions.\n"
      "if(NOT DEFINED CMAKE_OBJDUMP)\n"
      "  set(CMAKE_OBJDUMP \""
641
         << *command << "\")\n"
642
643
644
645
646
      "endif()\n"
      "\n";
    /* clang-format on */
  }

647
648
  this->AddGeneratorSpecificInstallSetup(fout);

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

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

669
  // Include install scripts from subdirectories.
670
671
672
673
674
675
676
  switch (status) {
    case cmPolicies::WARN:
      if (haveInstallAfterSubdirectory &&
          this->Makefile->PolicyOptionalWarningEnabled(
            "CMAKE_POLICY_WARNING_CMP0082")) {
        std::ostringstream e;
        e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0082) << "\n";
677
        this->IssueMessage(MessageType::AUTHOR_WARNING, e.str());
678
      }
679
680
681
682
683
684
685
686
687
688
689
      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);
690
            fout << "  include(\"" << odir << "/cmake_install.cmake\")\n";
691
692
693
694
695
696
697
698
699
700
701
702
703
          }
        }
        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;
704
  }
705
706

  // Record the install manifest.
707
  if (toplevel_install) {
708
    /* clang-format off */
709
    fout <<
710
711
      "if(CMAKE_INSTALL_COMPONENT)\n"
      "  set(CMAKE_INSTALL_MANIFEST \"install_manifest_"
712
      "${CMAKE_INSTALL_COMPONENT}.txt\")\n"
713
714
      "else()\n"
      "  set(CMAKE_INSTALL_MANIFEST \"install_manifest.txt\")\n"
715
716
717
718
719
720
      "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";
721
    /* clang-format on */
722
  }
723
724
}

725
726
void cmLocalGenerator::AddGeneratorTarget(
  std::unique_ptr<cmGeneratorTarget> gt)
727
{
728
729
730
731
732
  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);
733
734
}

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

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

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

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

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

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

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

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

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

  return true;
}

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

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

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

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

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

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

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

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

wahikihiki's avatar
wahikihiki committed
859
860
  std::string const& includeFlag =
    this->Makefile->GetSafeDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_", lang));
861
  bool quotePaths = false;
862
  if (this->Makefile->GetDefinition("CMAKE_QUOTE_INCLUDE_PATHS")) {
863
    quotePaths = true;
864
  }
Marc Chevrier's avatar
Marc Chevrier committed
865
  std::string sep = " ";
866
  bool repeatFlag = true;
Ken Martin's avatar
Ken Martin committed
867
  // should the include flag be repeated like ie. -IA -IB
Marc Chevrier's avatar
Marc Chevrier committed
868
  if (cmValue incSep = this->Makefile->GetDefinition(
Marc Chevrier's avatar
Marc Chevrier committed
869
        cmStrCat("CMAKE_INCLUDE_FLAG_SEP_", lang))) {
Ken Martin's avatar
Ken Martin committed
870
871
    // if there is a separator then the flag is not repeated but is only
    // given once i.e.  -classpath a:b:c
Marc Chevrier's avatar
Marc Chevrier committed
872
    sep = incSep;
873
    repeatFlag = false;
874
  }
875
876
877

  // Support special system include flag if it is available and the
  // normal flag is repeated for each directory.
Marc Chevrier's avatar
Marc Chevrier committed
878
879
  cmValue sysIncludeFlag = nullptr;
  cmValue sysIncludeFlagWarning = nullptr;
880
  if (repeatFlag) {
wahikihiki's avatar
wahikihiki committed
881
882
    sysIncludeFlag = this->Makefile->GetDefinition(
      cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", lang));
883
884
    sysIncludeFlagWarning = this->Makefile->GetDefinition(
      cmStrCat("_CMAKE_INCLUDE_SYSTEM_FLAG_", lang, "_WARNING"));
885
  }
886

Marc Chevrier's avatar