cmLocalGenerator.cxx 118 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
6
#include "cmsys/RegularExpression.hxx"

7
#include "cmAlgorithms.h"
8
#include "cmComputeLinkInformation.h"
9
#include "cmCustomCommand.h"
10
#include "cmCustomCommandGenerator.h"
11
#include "cmCustomCommandLines.h"
12
#include "cmCustomCommandTypes.h"
13
#include "cmGeneratedFileStream.h"
14
#include "cmGeneratorExpression.h"
15
#include "cmGeneratorExpressionEvaluationFile.h"
16
#include "cmGeneratorTarget.h"
Ken Martin's avatar
updates    
Ken Martin committed
17
#include "cmGlobalGenerator.h"
18
#include "cmInstallGenerator.h"
19
20
#include "cmInstallScriptGenerator.h"
#include "cmInstallTargetGenerator.h"
21
#include "cmLinkLineComputer.h"
22
#include "cmLinkLineDeviceComputer.h"
Ken Martin's avatar
Ken Martin committed
23
#include "cmMakefile.h"
24
#include "cmRulePlaceholderExpander.h"
25
#include "cmSourceFile.h"
26
#include "cmSourceFileLocation.h"
27
#include "cmSourceFileLocationKind.h"
28
29
#include "cmState.h"
#include "cmStateDirectory.h"
30
#include "cmStateTypes.h"
31
#include "cmStringAlgorithms.h"
32
33
#include "cmSystemTools.h"
#include "cmTarget.h"
34
#include "cmTestGenerator.h"
35
#include "cmVersion.h"
36
37
#include "cmake.h"

38
#if !defined(CMAKE_BOOTSTRAP)
39
40
#  define CM_LG_ENCODE_OBJECT_NAMES
#  include "cmCryptoHash.h"
41
42
#endif

43
#include <algorithm>
44
45
#include <cassert>
#include <cstdio>
46
#include <cstdlib>
47
#include <cstring>
48
#include <initializer_list>
49
50
#include <iterator>
#include <sstream>
51
#include <unordered_set>
52
#include <utility>
53
#include <vector>
54

55
56
#include <cm/string_view>

57
#if defined(__HAIKU__)
58
59
#  include <FindDirectory.h>
#  include <StorageDefs.h>
60
61
#endif

62
63
64
65
66
// 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.
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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" };
84

85
86
87
cmLocalGenerator::cmLocalGenerator(cmGlobalGenerator* gg, cmMakefile* makefile)
  : cmOutputConverter(makefile->GetStateSnapshot())
  , StateSnapshot(makefile->GetStateSnapshot())
88
  , DirectoryBacktrace(makefile->GetBacktrace())
Ken Martin's avatar
Ken Martin committed
89
{
90
  this->GlobalGenerator = gg;
91

92
  this->Makefile = makefile;
93

94
95
  this->AliasTargets = makefile->GetAliasTargets();

96
  this->EmitUniversalBinaryFlags = true;
97
98
  this->BackwardsCompatibility = 0;
  this->BackwardsCompatibilityFinal = false;
99
100

  this->ComputeObjectMaxPath();
101

102
103
104
105
106
107
108
109
110
111
112
113
114
  // 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");
    for (std::string& cp : cpath) {
      if (cmSystemTools::FileIsFullPath(cp)) {
        cp = cmSystemTools::CollapseFullPath(cp);
        this->EnvCPATH.emplace(std::move(cp));
      }
    }
  }

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

118
119
120
121
122
123
124
125
126
127
128
129
130
  if (const char* sysrootCompile =
        this->Makefile->GetDefinition("CMAKE_SYSROOT_COMPILE")) {
    this->CompilerSysroot = sysrootCompile;
  } else {
    this->CompilerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT");
  }

  if (const char* sysrootLink =
        this->Makefile->GetDefinition("CMAKE_SYSROOT_LINK")) {
    this->LinkerSysroot = sysrootLink;
  } else {
    this->LinkerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT");
  }
131

132
  for (std::string const& lang : enabledLanguages) {
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
    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);

165
166
167
    for (std::string replaceVar : ruleReplaceVars) {
      if (replaceVar.find("${LANG}") != std::string::npos) {
        cmSystemTools::ReplaceString(replaceVar, "${LANG}", lang);
168
169
      }

170
171
      this->VariableMappings[replaceVar] =
        this->Makefile->GetSafeDefinition(replaceVar);
172
173
    }
  }
Ken Martin's avatar
Ken Martin committed
174
175
}

176
177
178
179
cmRulePlaceholderExpander* cmLocalGenerator::CreateRulePlaceholderExpander()
  const
{
  return new cmRulePlaceholderExpander(this->Compilers, this->VariableMappings,
180
181
                                       this->CompilerSysroot,
                                       this->LinkerSysroot);
182
183
}

Ken Martin's avatar
Ken Martin committed
184
185
cmLocalGenerator::~cmLocalGenerator()
{
186
187
  cmDeleteAll(this->GeneratorTargets);
  cmDeleteAll(this->OwnedImportedGeneratorTargets);
Ken Martin's avatar
Ken Martin committed
188
189
}

190
void cmLocalGenerator::IssueMessage(MessageType t,
191
192
                                    std::string const& text) const
{
193
  this->GetCMakeInstance()->IssueMessage(t, text, this->DirectoryBacktrace);
194
195
}

196
197
void cmLocalGenerator::ComputeObjectMaxPath()
{
198
// Choose a maximum object file name length.
199
200
201
202
203
204
#if defined(_WIN32) || defined(__CYGWIN__)
  this->ObjectPathMax = 250;
#else
  this->ObjectPathMax = 1000;
#endif
  const char* plen = this->Makefile->GetDefinition("CMAKE_OBJECT_PATH_MAX");
205
  if (plen && *plen) {
206
    unsigned int pmax;
207
208
    if (sscanf(plen, "%u", &pmax) == 1) {
      if (pmax >= 128) {
209
        this->ObjectPathMax = pmax;
210
      } else {
211
        std::ostringstream w;
212
213
214
        w << "CMAKE_OBJECT_PATH_MAX is set to " << pmax
          << ", which is less than the minimum of 128.  "
          << "The value will be ignored.";
215
        this->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
216
      }
217
    } else {
218
      std::ostringstream w;
219
220
221
      w << "CMAKE_OBJECT_PATH_MAX is set to \"" << plen
        << "\", which fails to parse as a positive integer.  "
        << "The value will be ignored.";
222
      this->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
223
    }
224
  }
225
  this->ObjectMaxPathViolations.clear();
226
227
}

228
229
230
231
static void MoveSystemIncludesToEnd(std::vector<std::string>& includeDirs,
                                    const std::string& config,
                                    const std::string& lang,
                                    const cmGeneratorTarget* target)
232
233
234
235
236
237
238
239
240
241
242
243
244
{
  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);
    });
}

245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
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);
                   });
}

Alexander Neundorf's avatar
   
Alexander Neundorf committed
263
264
265
void cmLocalGenerator::TraceDependencies()
{
  // Generate the rule files for each target.
266
  const std::vector<cmGeneratorTarget*>& targets = this->GetGeneratorTargets();
267
268
  for (cmGeneratorTarget* target : targets) {
    if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
269
      continue;
Alexander Neundorf's avatar
   
Alexander Neundorf committed
270
    }
271
    target->TraceDependencies();
272
  }
Alexander Neundorf's avatar
   
Alexander Neundorf committed
273
274
}

275
276
void cmLocalGenerator::GenerateTestFiles()
{
277
  if (!this->Makefile->IsOn("CMAKE_TESTING_ENABLED")) {
278
    return;
279
  }
280
281
282

  // Compute the set of configurations.
  std::vector<std::string> configurationTypes;
283
  const std::string& config =
284
    this->Makefile->GetConfigurations(configurationTypes, false);
285

286
287
288
  std::string file =
    cmStrCat(this->StateSnapshot.GetDirectory().GetCurrentBinary(),
             "/CTestTestfile.cmake");
289

290
  cmGeneratedFileStream fout(file);
291
292
293
  fout.SetCopyIfDifferent(true);

  fout << "# CMake generated Testfile for " << std::endl
294
       << "# Source directory: "
295
       << this->StateSnapshot.GetDirectory().GetCurrentSource() << std::endl
296
       << "# Build directory: "
297
       << this->StateSnapshot.GetDirectory().GetCurrentBinary() << std::endl
Ken Martin's avatar
Ken Martin committed
298
       << "# " << std::endl
299
       << "# This file includes the relevant testing commands "
Ken Martin's avatar
Ken Martin committed
300
301
302
       << "required for " << std::endl
       << "# testing this directory and lists subdirectories to "
       << "be tested as well." << std::endl;
303
304

  const char* testIncludeFile =
Ken Martin's avatar
Ken Martin committed
305
    this->Makefile->GetProperty("TEST_INCLUDE_FILE");
306
  if (testIncludeFile) {
307
    fout << "include(\"" << testIncludeFile << "\")" << std::endl;
308
  }
309

Matthew Woehlke's avatar
Matthew Woehlke committed
310
311
312
  const char* testIncludeFiles =
    this->Makefile->GetProperty("TEST_INCLUDE_FILES");
  if (testIncludeFiles) {
313
    std::vector<std::string> includesList = cmExpandedList(testIncludeFiles);
314
315
    for (std::string const& i : includesList) {
      fout << "include(\"" << i << "\")" << std::endl;
Matthew Woehlke's avatar
Matthew Woehlke committed
316
317
318
    }
  }

319
  // Ask each test generator to write its code.
320
321
  std::vector<cmTestGenerator*> const& testers =
    this->Makefile->GetTestGenerators();
322
323
324
  for (cmTestGenerator* tester : testers) {
    tester->Compute(this);
    tester->Generate(fout, config, configurationTypes);
325
  }
326
  using vec_t = std::vector<cmStateSnapshot>;
327
  vec_t const& children = this->Makefile->GetStateSnapshot().GetChildren();
328
  std::string parentBinDir = this->GetCurrentBinaryDirectory();
329
  for (cmStateSnapshot const& i : children) {
330
    // TODO: Use add_subdirectory instead?
331
    std::string outP = i.GetDirectory().GetCurrentBinary();
332
    outP = this->MaybeConvertToRelativePath(parentBinDir, outP);
333
    outP = cmOutputConverter::EscapeForCMake(outP);
334
    fout << "subdirs(" << outP << ")" << std::endl;
335
  }
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354

  // Add directory labels property
  const char* directoryLabels =
    this->Makefile->GetDefinition("CMAKE_DIRECTORY_LABELS");
  const char* labels = this->Makefile->GetProperty("LABELS");

  if (labels || directoryLabels) {
    fout << "set_directory_properties(PROPERTIES LABELS ";
    if (labels) {
      fout << cmOutputConverter::EscapeForCMake(labels);
    }
    if (labels && directoryLabels) {
      fout << ";";
    }
    if (directoryLabels) {
      fout << cmOutputConverter::EscapeForCMake(directoryLabels);
    }
    fout << ")" << std::endl;
  }
355
356
}

357
358
359
360
361
362
363
364
365
void cmLocalGenerator::CreateEvaluationFileOutputs()
{
  std::vector<std::string> const& configs =
    this->Makefile->GetGeneratorConfigs();
  for (std::string const& c : configs) {
    this->CreateEvaluationFileOutputs(c);
  }
}

366
367
368
void cmLocalGenerator::CreateEvaluationFileOutputs(std::string const& config)
{
  std::vector<cmGeneratorExpressionEvaluationFile*> ef =
369
    this->Makefile->GetEvaluationFiles();
370
371
  for (cmGeneratorExpressionEvaluationFile* geef : ef) {
    geef->CreateOutputFile(this, config);
372
  }
373
374
375
}

void cmLocalGenerator::ProcessEvaluationFiles(
376
  std::vector<std::string>& generatedFiles)
377
{
378
379
  for (cmGeneratorExpressionEvaluationFile* geef :
       this->Makefile->GetEvaluationFiles()) {
380
    geef->Generate(this);
381
    if (cmSystemTools::GetFatalErrorOccured()) {
382
      return;
383
    }
384
    std::vector<std::string> files = geef->GetFiles();
385
386
387
    std::sort(files.begin(), files.end());

    std::vector<std::string> intersection;
388
389
    std::set_intersection(files.begin(), files.end(), generatedFiles.begin(),
                          generatedFiles.end(),
390
                          std::back_inserter(intersection));
391
    if (!intersection.empty()) {
392
      cmSystemTools::Error("Files to be generated by multiple different "
393
394
                           "commands: " +
                           cmWrap('"', intersection, '"', " "));
395
      return;
396
    }
397

398
399
400
401
    cmAppend(generatedFiles, files);
    std::inplace_merge(generatedFiles.begin(),
                       generatedFiles.end() - files.size(),
                       generatedFiles.end());
402
  }
403
404
}

405
406
void cmLocalGenerator::GenerateInstallRules()
{
407
  // Compute the install prefix.
Ken Martin's avatar
Ken Martin committed
408
  const char* prefix = this->Makefile->GetDefinition("CMAKE_INSTALL_PREFIX");
409

410
411
#if defined(_WIN32) && !defined(__CYGWIN__)
  std::string prefix_win32;
412
413
  if (!prefix) {
    if (!cmSystemTools::GetEnv("SystemDrive", prefix_win32)) {
414
      prefix_win32 = "C:";
415
    }
Ken Martin's avatar
Ken Martin committed
416
    const char* project_name = this->Makefile->GetDefinition("PROJECT_NAME");
417
    if (project_name && project_name[0]) {
418
419
      prefix_win32 += "/Program Files/";
      prefix_win32 += project_name;
420
    } else {
421
422
      prefix_win32 += "/InstalledCMakeProject";
    }
423
424
    prefix = prefix_win32.c_str();
  }
425
#elif defined(__HAIKU__)
426
  char dir[B_PATH_NAME_LENGTH];
427
428
429
  if (!prefix) {
    if (find_directory(B_SYSTEM_DIRECTORY, -1, false, dir, sizeof(dir)) ==
        B_OK) {
430
      prefix = dir;
431
    } else {
432
      prefix = "/boot/system";
433
    }
434
  }
435
#else
436
  if (!prefix) {
437
    prefix = "/usr/local";
438
  }
439
#endif
440
441
  if (const char* stagingPrefix =
        this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX")) {
442
    prefix = stagingPrefix;
443
  }
444

445
446
  // Compute the set of configurations.
  std::vector<std::string> configurationTypes;
447
  const std::string& config =
448
    this->Makefile->GetConfigurations(configurationTypes, false);
449

450
  // Choose a default install configuration.
451
  std::string default_config = config;
452
  const char* default_order[] = { "RELEASE", "MINSIZEREL", "RELWITHDEBINFO",
Daniel Pfeifer's avatar
Daniel Pfeifer committed
453
                                  "DEBUG", nullptr };
454
  for (const char** c = default_order; *c && default_config.empty(); ++c) {
455
456
457
    for (std::string const& configurationType : configurationTypes) {
      if (cmSystemTools::UpperCase(configurationType) == *c) {
        default_config = configurationType;
458
459
      }
    }
460
461
  }
  if (default_config.empty() && !configurationTypes.empty()) {
462
    default_config = configurationTypes[0];
463
  }
464

465
  // Create the install script file.
466
  std::string file = this->StateSnapshot.GetDirectory().GetCurrentBinary();
467
  std::string homedir = this->GetState()->GetBinaryDirectory();
468
  int toplevel_install = 0;
469
  if (file == homedir) {
470
    toplevel_install = 1;
471
  }
472
  file += "/cmake_install.cmake";
473
  cmGeneratedFileStream fout(file);
474
  fout.SetCopyIfDifferent(true);
475

476
477
  // Write the header.
  fout << "# Install script for directory: "
478
479
       << this->StateSnapshot.GetDirectory().GetCurrentSource() << std::endl
       << std::endl;
Ken Martin's avatar
Ken Martin committed
480
  fout << "# Set the install prefix" << std::endl
481
482
483
       << "if(NOT DEFINED CMAKE_INSTALL_PREFIX)" << std::endl
       << "  set(CMAKE_INSTALL_PREFIX \"" << prefix << "\")" << std::endl
       << "endif()" << std::endl
484
       << R"(string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX )"
Ken Martin's avatar
Ken Martin committed
485
486
       << "\"${CMAKE_INSTALL_PREFIX}\")" << std::endl
       << std::endl;
487

488
  // Write support code for generating per-configuration install rules.
489
  /* clang-format off */
490
491
  fout <<
    "# Set the install configuration name.\n"
492
493
494
    "if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)\n"
    "  if(BUILD_TYPE)\n"
    "    string(REGEX REPLACE \"^[^A-Za-z0-9_]+\" \"\"\n"
495
    "           CMAKE_INSTALL_CONFIG_NAME \"${BUILD_TYPE}\")\n"
496
497
498
499
    "  else()\n"
    "    set(CMAKE_INSTALL_CONFIG_NAME \"" << default_config << "\")\n"
    "  endif()\n"
    "  message(STATUS \"Install configuration: "
Ken Martin's avatar
Ken Martin committed
500
    "\\\"${CMAKE_INSTALL_CONFIG_NAME}\\\"\")\n"
501
    "endif()\n"
502
    "\n";
503
  /* clang-format on */
504

505
  // Write support code for dealing with component-specific installs.
506
  /* clang-format off */
507
508
  fout <<
    "# Set the component getting installed.\n"
509
510
511
512
513
514
515
516
    "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"
517
    "\n";
518
  /* clang-format on */
519

520
  // Copy user-specified install options to the install code.
521
522
  if (const char* so_no_exe =
        this->Makefile->GetDefinition("CMAKE_INSTALL_SO_NO_EXE")) {
523
    /* clang-format off */
524
525
    fout <<
      "# Install shared libraries without execute permission?\n"
526
527
528
      "if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)\n"
      "  set(CMAKE_INSTALL_SO_NO_EXE \"" << so_no_exe << "\")\n"
      "endif()\n"
529
      "\n";
530
    /* clang-format on */
531
  }
532

533
534
535
536
537
538
539
540
541
542
543
544
545
  // Copy cmake cross compile state to install code.
  if (const char* crosscompiling =
        this->Makefile->GetDefinition("CMAKE_CROSSCOMPILING")) {
    /* clang-format off */
    fout <<
      "# Is this installation the result of a crosscompile?\n"
      "if(NOT DEFINED CMAKE_CROSSCOMPILING)\n"
      "  set(CMAKE_CROSSCOMPILING \"" << crosscompiling << "\")\n"
      "endif()\n"
      "\n";
    /* clang-format on */
  }

546
547
548
549
550
551
552
553
554
555
556
557
558
559
  // Write default directory permissions.
  if (const char* defaultDirPermissions = this->Makefile->GetDefinition(
        "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 \""
         << defaultDirPermissions << "\")\n"
      "endif()\n"
      "\n";
    /* clang-format on */
  }

560
  // Ask each install generator to write its code.
561
  cmPolicies::PolicyStatus status = this->GetPolicyStatus(cmPolicies::CMP0082);
562
  std::vector<cmInstallGenerator*> const& installers =
Ken Martin's avatar
Ken Martin committed
563
    this->Makefile->GetInstallGenerators();
564
565
566
567
568
569
570
571
572
573
574
575
  bool haveSubdirectoryInstall = false;
  bool haveInstallAfterSubdirectory = false;
  if (status == cmPolicies::WARN) {
    for (cmInstallGenerator* installer : installers) {
      installer->CheckCMP0082(haveSubdirectoryInstall,
                              haveInstallAfterSubdirectory);
      installer->Generate(fout, config, configurationTypes);
    }
  } else {
    for (cmInstallGenerator* installer : installers) {
      installer->Generate(fout, config, configurationTypes);
    }
576
  }
577

578
579
  // Write rules from old-style specification stored in targets.
  this->GenerateTargetInstallRules(fout, config, configurationTypes);
580

581
  // Include install scripts from subdirectories.
582
583
584
585
586
587
588
  switch (status) {
    case cmPolicies::WARN:
      if (haveInstallAfterSubdirectory &&
          this->Makefile->PolicyOptionalWarningEnabled(
            "CMAKE_POLICY_WARNING_CMP0082")) {
        std::ostringstream e;
        e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0082) << "\n";
589
        this->IssueMessage(MessageType::AUTHOR_WARNING, e.str());
590
      }
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
      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);
            fout << "  include(\"" << odir << "/cmake_install.cmake\")"
                 << std::endl;
          }
        }
        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;
617
  }
618
619

  // Record the install manifest.
620
  if (toplevel_install) {
621
    /* clang-format off */
622
    fout <<
623
624
      "if(CMAKE_INSTALL_COMPONENT)\n"
      "  set(CMAKE_INSTALL_MANIFEST \"install_manifest_"
625
      "${CMAKE_INSTALL_COMPONENT}.txt\")\n"
626
627
      "else()\n"
      "  set(CMAKE_INSTALL_MANIFEST \"install_manifest.txt\")\n"
628
629
630
631
632
633
      "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";
634
    /* clang-format on */
635
  }
636
637
}

638
void cmLocalGenerator::AddGeneratorTarget(cmGeneratorTarget* gt)
639
{
640
  this->GeneratorTargets.push_back(gt);
641
  this->GeneratorTargetSearchIndex.emplace(gt->GetName(), gt);
642
  this->GlobalGenerator->IndexGeneratorTarget(gt);
643
644
}

645
646
void cmLocalGenerator::AddImportedGeneratorTarget(cmGeneratorTarget* gt)
{
647
  this->ImportedGeneratorTargets.emplace(gt->GetName(), gt);
648
  this->GlobalGenerator->IndexGeneratorTarget(gt);
649
650
}

651
652
653
654
655
void cmLocalGenerator::AddOwnedImportedGeneratorTarget(cmGeneratorTarget* gt)
{
  this->OwnedImportedGeneratorTargets.push_back(gt);
}

656
cmGeneratorTarget* cmLocalGenerator::FindLocalNonAliasGeneratorTarget(
657
  const std::string& name) const
658
{
wahikihiki's avatar
wahikihiki committed
659
  auto ti = this->GeneratorTargetSearchIndex.find(name);
660
661
  if (ti != this->GeneratorTargetSearchIndex.end()) {
    return ti->second;
662
  }
Daniel Pfeifer's avatar
Daniel Pfeifer committed
663
  return nullptr;
664
665
}

666
void cmLocalGenerator::ComputeTargetManifest()
667
668
669
{
  // Collect the set of configuration types.
  std::vector<std::string> configNames;
670
  this->Makefile->GetConfigurations(configNames);
671
  if (configNames.empty()) {
672
    configNames.emplace_back();
673
  }
674
675

  // Add our targets to the manifest for each configuration.
676
  const std::vector<cmGeneratorTarget*>& targets = this->GetGeneratorTargets();
677
  for (cmGeneratorTarget* target : targets) {
678
    if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
679
      continue;
680
    }
681
682
    for (std::string const& c : configNames) {
      target->ComputeTargetManifest(c);
683
    }
684
  }
685
686
}

687
688
689
690
691
692
bool cmLocalGenerator::ComputeTargetCompileFeatures()
{
  // Collect the set of configuration types.
  std::vector<std::string> configNames;
  this->Makefile->GetConfigurations(configNames);
  if (configNames.empty()) {
693
    configNames.emplace_back();
694
695
  }

696
697
698
699
700
701
702
703
704
705
  using LanguagePair = std::pair<std::string, std::string>;
  std::vector<LanguagePair> pairedLanguages{ { "OBJC", "C" },
                                             { "OBJCXX", "CXX" } };
  std::set<LanguagePair> objcEnabledLanguages;
  for (auto const& lang : pairedLanguages) {
    if (this->Makefile->GetState()->GetLanguageEnabled(lang.first)) {
      objcEnabledLanguages.insert(lang);
    }
  }

706
  // Process compile features of all targets.
707
  const std::vector<cmGeneratorTarget*>& targets = this->GetGeneratorTargets();
708
709
710
  for (cmGeneratorTarget* target : targets) {
    for (std::string const& c : configNames) {
      if (!target->ComputeCompileFeatures(c)) {
711
712
713
        return false;
      }
    }
714
715
716

    // Now that C/C++ _STANDARD values have been computed
    // set the values to ObjC/ObjCXX _STANDARD variables
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
    if (target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
      auto copyStandardToObjLang = [&](LanguagePair const& lang) -> bool {
        if (!target->GetProperty(cmStrCat(lang.first, "_STANDARD"))) {
          auto* standard =
            target->GetProperty(cmStrCat(lang.second, "_STANDARD"));
          if (!standard) {
            standard = this->Makefile->GetDefinition(
              cmStrCat("CMAKE_", lang.second, "_STANDARD_DEFAULT"));
          }
          target->Target->SetProperty(cmStrCat(lang.first, "_STANDARD"),
                                      standard);
          return true;
        }
        return false;
      };
      auto copyPropertyToObjLang = [&](LanguagePair const& lang,
                                       const char* property) {
        if (!target->GetProperty(cmStrCat(lang.first, property)) &&
            target->GetProperty(cmStrCat(lang.second, property))) {
          target->Target->SetProperty(
            cmStrCat(lang.first, property),
            target->GetProperty(cmStrCat(lang.second, property)));
        }
      };
      for (auto const& lang : objcEnabledLanguages) {
        if (copyStandardToObjLang(lang)) {
          copyPropertyToObjLang(lang, "_STANDARD_REQUIRED");
          copyPropertyToObjLang(lang, "_EXTENSIONS");
        }
746
747
      }
    }
748
749
750
751
752
  }

  return true;
}

753
754
755
756
757
bool cmLocalGenerator::IsRootMakefile() const
{
  return !this->StateSnapshot.GetBuildsystemDirectoryParent().IsValid();
}

758
759
760
761
762
cmState* cmLocalGenerator::GetState() const
{
  return this->GlobalGenerator->GetCMakeInstance()->GetState();
}

763
cmStateSnapshot cmLocalGenerator::GetStateSnapshot() const
764
{
765
  return this->Makefile->GetStateSnapshot();
766
767
}

768
const char* cmLocalGenerator::GetRuleLauncher(cmGeneratorTarget* target,
769
                                              const std::string& prop)
770
{
771
  if (target) {
772
    return target->GetProperty(prop);
773
  }
774
  return this->Makefile->GetProperty(prop);
775
776
}

777
778
std::string cmLocalGenerator::ConvertToIncludeReference(
  std::string const& path, OutputFormat format, bool forceFullPaths)
779
{
780
781
  static_cast<void>(forceFullPaths);
  return this->ConvertToOutputForExisting(path, format);
782
783
}

784
std::string cmLocalGenerator::GetIncludeFlags(
785
  const std::vector<std::string>& includeDirs, cmGeneratorTarget* target,
786
787
788
789
  const std::string& lang, bool forceFullPaths, bool forResponseFile,
  const std::string& config)
{
  if (lang.empty()) {
790
    return "";
791
  }
792

793
  std::vector<std::string> includes = includeDirs;
794
  MoveSystemIncludesToEnd(includes, config, lang, target);
795

796
  OutputFormat shellFormat = forResponseFile ? RESPONSE : SHELL;
797
  std::ostringstream includeFlags;
798

wahikihiki's avatar
wahikihiki committed
799
800
801
802
  std::string const& includeFlag =
    this->Makefile->GetSafeDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_", lang));
  const char* sep =
    this->Makefile->GetDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_SEP_", lang));
803
  bool quotePaths = false;
804
  if (this->Makefile->GetDefinition("CMAKE_QUOTE_INCLUDE_PATHS")) {
805
    quotePaths = true;
806
  }
807
  bool repeatFlag = true;
Ken Martin's avatar
Ken Martin committed
808
  // should the include flag be repeated like ie. -IA -IB
809
  if (!sep) {
810
    sep = " ";
811
  } else {
Ken Martin's avatar
Ken Martin committed
812
813
    // if there is a separator then the flag is not repeated but is only
    // given once i.e.  -classpath a:b:c
814
    repeatFlag = false;
815
  }
816
817
818

  // Support special system include flag if it is available and the
  // normal flag is repeated for each directory.
Daniel Pfeifer's avatar
Daniel Pfeifer committed
819
  const char* sysIncludeFlag = nullptr;
820
  if (repeatFlag) {
wahikihiki's avatar
wahikihiki committed
821
822
    sysIncludeFlag = this->Makefile->GetDefinition(
      cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", lang));
823
  }
824

wahikihiki's avatar
wahikihiki committed
825
826
827
828
  const char* fwSearchFlag = this->Makefile->GetDefinition(
    cmStrCat("CMAKE_", lang, "_FRAMEWORK_SEARCH_FLAG"));
  const char* sysFwSearchFlag = this->Makefile->GetDefinition(
    cmStrCat("CMAKE_", lang, "_SYSTEM_FRAMEWORK_SEARCH_FLAG"));
829

830
  bool flagUsed = false;
831
  std::set<std::string> emitted;
832
833
834
#ifdef __APPLE__
  emitted.insert("/System/Library/Frameworks");
#endif
835
  for (std::string const& i : includes) {
836
    if (fwSearchFlag && *fwSearchFlag && this->Makefile->IsOn("APPLE") &&
837
        cmSystemTools::IsPathToFramework(i)) {
wahikihiki's avatar
wahikihiki committed
838
839
      std::string const frameworkDir =
        cmSystemTools::CollapseFullPath(cmStrCat(i, "/../"));
840
      if (emitted.insert(frameworkDir).second) {
841
        if (sysFwSearchFlag && target &&
842
            target->IsSystemIncludeDirectory(i, config, lang)) {
843
          includeFlags << sysFwSearchFlag;
844
        } else {
845
          includeFlags << fwSearchFlag;
846
        }
847
848
        includeFlags << this->ConvertToOutputFormat(frameworkDir, shellFormat)
                     << " ";
849
      }
850
851
      continue;
    }
852

853
854
    if (!flagUsed || repeatFlag) {
      if (sysIncludeFlag && target &&
855
          target->IsSystemIncludeDirectory(i, config, lang)) {
856
        includeFlags << sysIncludeFlag;
857
      } else {
858
        includeFlags << includeFlag;
859
      }
860
861
      flagUsed = true;
    }
862
    std::string includePath =
863
      this->ConvertToIncludeReference(i, shellFormat, forceFullPaths);
864
    if (quotePaths && !includePath.empty() && includePath.front() != '\"') {
865
      includeFlags << "\"";
866
    }
867
    includeFlags << includePath;
868
    if (quotePaths && !includePath.empty() && includePath.front() != '\"') {
869
      includeFlags << "\"";
870
    }
871
872
    includeFlags << sep;
  }
873
874
  std::string flags = includeFlags.str();
  // remove trailing separators
875
876
  if ((sep[0] != ' ') && !flags.empty() && flags.back() == sep[0]) {
    flags.back() = ' ';
877
  }
878
  return flags;