Coverage Report

Created: 2026-07-14 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmGlobalUnixMakefileGenerator3.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
#include "cmGlobalUnixMakefileGenerator3.h"
4
5
#include <algorithm>
6
#include <functional>
7
#include <sstream>
8
#include <utility>
9
10
#include <cm/memory>
11
#include <cmext/algorithm>
12
#include <cmext/memory>
13
14
#include "cmGeneratedFileStream.h"
15
#include "cmGeneratorTarget.h"
16
#include "cmGlobalGenerator.h"
17
#include "cmLocalGenerator.h"
18
#include "cmLocalUnixMakefileGenerator3.h"
19
#include "cmMakefile.h"
20
#include "cmMakefileTargetGenerator.h"
21
#include "cmMessageType.h"
22
#include "cmOutputConverter.h"
23
#include "cmState.h"
24
#include "cmStringAlgorithms.h"
25
#include "cmSystemTools.h"
26
#include "cmTarget.h"
27
#include "cmTargetDepend.h"
28
#include "cmTargetTypes.h"
29
#include "cmTest.h"
30
#include "cmTestGenerator.h"
31
#include "cmValue.h"
32
#include "cmake.h"
33
34
cmGlobalUnixMakefileGenerator3::cmGlobalUnixMakefileGenerator3(cmake* cm)
35
0
  : cmGlobalCommonGenerator(cm)
36
0
{
37
  // This type of makefile always requires unix style paths
38
0
  this->ForceUnixPaths = true;
39
0
  this->FindMakeProgramFile = "CMakeUnixFindMake.cmake";
40
0
  this->ToolSupportsColor = true;
41
42
#if defined(_WIN32) || defined(__VMS)
43
  this->UseLinkScript = false;
44
#else
45
0
  this->UseLinkScript = true;
46
0
#endif
47
48
0
  this->IncludeDirective = "include";
49
0
  this->LineContinueDirective = "\\\n";
50
0
  this->DefineWindowsNULL = false;
51
0
  this->PassMakeflags = false;
52
0
  this->UnixCD = true;
53
0
}
54
55
0
cmGlobalUnixMakefileGenerator3::~cmGlobalUnixMakefileGenerator3() = default;
56
57
void cmGlobalUnixMakefileGenerator3::EnableLanguage(
58
  std::vector<std::string> const& languages, cmMakefile* mf, bool optional)
59
0
{
60
0
  this->cmGlobalGenerator::EnableLanguage(languages, mf, optional);
61
0
  for (std::string const& language : languages) {
62
0
    if (language == "NONE") {
63
0
      continue;
64
0
    }
65
0
    this->ResolveLanguageCompiler(language, mf, optional);
66
0
  }
67
0
}
68
69
//! Create a local generator appropriate to this Global Generator
70
std::unique_ptr<cmLocalGenerator>
71
cmGlobalUnixMakefileGenerator3::CreateLocalGenerator(cmMakefile* mf)
72
0
{
73
0
  return std::unique_ptr<cmLocalGenerator>(
74
0
    cm::make_unique<cmLocalUnixMakefileGenerator3>(this, mf));
75
0
}
76
77
cmDocumentationEntry cmGlobalUnixMakefileGenerator3::GetDocumentation()
78
0
{
79
0
  return { cmGlobalUnixMakefileGenerator3::GetActualName(),
80
0
           "Generates standard UNIX makefiles." };
81
0
}
82
83
bool cmGlobalUnixMakefileGenerator3::SupportsShortObjectNames() const
84
0
{
85
0
  return true;
86
0
}
87
88
void cmGlobalUnixMakefileGenerator3::ComputeTargetObjectDirectory(
89
  cmGeneratorTarget* gt) const
90
0
{
91
  // Compute full path to object file directory for this target.
92
0
  std::string dir = cmStrCat(gt->GetSupportDirectory(), '/');
93
0
  gt->ObjectDirectory = dir;
94
0
}
95
96
bool cmGlobalUnixMakefileGenerator3::CanEscapeOctothorpe() const
97
0
{
98
  // Make tools that use UNIX-style '/' paths also support '\' escaping.
99
0
  return this->ForceUnixPaths;
100
0
}
101
102
void cmGlobalUnixMakefileGenerator3::Configure()
103
0
{
104
  // Initialize CMAKE_EDIT_COMMAND cache entry.
105
0
  this->GetEditCacheCommand();
106
107
0
  this->cmGlobalGenerator::Configure();
108
0
}
109
110
void cmGlobalUnixMakefileGenerator3::Generate()
111
0
{
112
0
  this->ClangTidyExportFixesDirs.clear();
113
0
  this->ClangTidyExportFixesFiles.clear();
114
115
  // Compute the "test_prep/" targets before generating the local makefiles
116
  // so their convenience rules can be written into the top-level Makefile.
117
0
  this->ComputeTestPrepTargets();
118
119
  // first do superclass method
120
0
  this->cmGlobalGenerator::Generate();
121
122
  // initialize progress
123
0
  unsigned long total = 0;
124
0
  for (auto const& pmi : this->ProgressMap) {
125
0
    total += pmi.second.NumberOfActions;
126
0
  }
127
128
  // write each target's progress.make this loop is done twice. Basically the
129
  // Generate pass counts all the actions, the first loop below determines
130
  // how many actions have progress updates for each target and writes to
131
  // correct variable values for everything except the all targets. The
132
  // second loop actually writes out correct values for the all targets as
133
  // well. This is because the all targets require more information that is
134
  // computed in the first loop.
135
0
  unsigned long current = 0;
136
0
  for (auto& pmi : this->ProgressMap) {
137
0
    pmi.second.WriteProgressVariables(total, current);
138
0
  }
139
0
  for (auto const& lg : this->LocalGenerators) {
140
0
    std::string markFileName =
141
0
      cmStrCat(lg->GetCurrentBinaryDirectory(), "/CMakeFiles/progress.marks");
142
0
    cmGeneratedFileStream markFile(markFileName);
143
0
    markFile << this->CountProgressMarksInAll(*lg) << "\n";
144
0
  }
145
146
  // write the main makefile
147
0
  this->WriteMainMakefile2();
148
0
  this->WriteMainCMakefile();
149
150
0
  if (this->CommandDatabase) {
151
0
    *this->CommandDatabase << "\n]";
152
0
    this->CommandDatabase.reset();
153
0
  }
154
155
0
  this->RemoveUnknownClangTidyExportFixesFiles();
156
0
}
157
158
void cmGlobalUnixMakefileGenerator3::AddCXXCompileCommand(
159
  std::string const& sourceFile, std::string const& workingDirectory,
160
  std::string const& compileCommand, std::string const& objPath)
161
0
{
162
0
  if (!this->CommandDatabase) {
163
0
    std::string commandDatabaseName =
164
0
      this->GetCMakeInstance()->GetHomeOutputDirectory() +
165
0
      "/compile_commands.json";
166
0
    this->CommandDatabase =
167
0
      cm::make_unique<cmGeneratedFileStream>(commandDatabaseName);
168
0
    *this->CommandDatabase << "[\n";
169
0
  } else {
170
0
    *this->CommandDatabase << ",\n";
171
0
  }
172
0
  *this->CommandDatabase << "{\n"
173
0
                         << R"(  "directory": ")"
174
0
                         << cmGlobalGenerator::EscapeJSON(workingDirectory)
175
0
                         << "\",\n"
176
0
                         << R"(  "command": ")"
177
0
                         << cmGlobalGenerator::EscapeJSON(compileCommand)
178
0
                         << "\",\n"
179
0
                         << R"(  "file": ")"
180
0
                         << cmGlobalGenerator::EscapeJSON(sourceFile)
181
0
                         << "\",\n"
182
0
                         << R"(  "output": ")"
183
0
                         << cmGlobalGenerator::EscapeJSON(objPath) << "\"\n}";
184
0
}
185
186
void cmGlobalUnixMakefileGenerator3::WriteMainMakefile2()
187
0
{
188
  // Open the output file.  This should not be copy-if-different
189
  // because the check-build-system step compares the makefile time to
190
  // see if the build system must be regenerated.
191
0
  std::string makefileName =
192
0
    cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(),
193
0
             "/CMakeFiles/Makefile2");
194
0
  cmGeneratedFileStream makefileStream(makefileName, false,
195
0
                                       this->GetMakefileEncoding());
196
0
  if (!makefileStream) {
197
0
    return;
198
0
  }
199
200
  // The global dependency graph is expressed via the root local generator.
201
0
  auto& rootLG = cm::static_reference_cast<cmLocalUnixMakefileGenerator3>(
202
0
    this->LocalGenerators[0]);
203
204
  // Write the do not edit header.
205
0
  rootLG.WriteDisclaimer(makefileStream);
206
207
  // Write the main entry point target.  This must be the VERY first
208
  // target so that make with no arguments will run it.
209
  // Just depend on the all target to drive the build.
210
0
  std::vector<std::string> depends;
211
0
  std::vector<std::string> no_commands;
212
0
  depends.emplace_back("all");
213
214
  // Write the rule.
215
0
  rootLG.WriteMakeRule(makefileStream,
216
0
                       "Default target executed when no arguments are "
217
0
                       "given to make.",
218
0
                       "default_target", depends, no_commands, true);
219
220
0
  depends.clear();
221
222
  // The all and preinstall rules might never have any dependencies
223
  // added to them.
224
0
  if (!this->EmptyRuleHackDepends.empty()) {
225
0
    depends.push_back(this->EmptyRuleHackDepends);
226
0
  }
227
228
  // Write out the "special" stuff
229
0
  rootLG.WriteSpecialTargetsTop(makefileStream);
230
231
  // Write the directory level rules.
232
0
  for (auto const& it : this->ComputeDirectoryTargets()) {
233
0
    this->WriteDirectoryRules2(makefileStream, rootLG, it.second);
234
0
  }
235
236
  // Write the target convenience rules
237
0
  for (auto const& localGen : this->LocalGenerators) {
238
0
    this->WriteConvenienceRules2(
239
0
      makefileStream, rootLG,
240
0
      cm::static_reference_cast<cmLocalUnixMakefileGenerator3>(localGen));
241
0
  }
242
243
  // Write the internal test_prep/ rules.
244
0
  this->WriteTestPrepRules(makefileStream, rootLG);
245
246
  // Write special bottom targets
247
0
  rootLG.WriteSpecialTargetsBottom(makefileStream);
248
0
}
249
250
void cmGlobalUnixMakefileGenerator3::WriteMainCMakefile()
251
0
{
252
0
  if (this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) {
253
0
    return;
254
0
  }
255
256
  // Open the output file.  This should not be copy-if-different
257
  // because the check-build-system step compares the makefile time to
258
  // see if the build system must be regenerated.
259
0
  std::string cmakefileName =
260
0
    cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(),
261
0
             "/CMakeFiles/Makefile.cmake");
262
0
  cmGeneratedFileStream cmakefileStream(cmakefileName);
263
0
  if (!cmakefileStream) {
264
0
    return;
265
0
  }
266
267
0
  std::string makefileName =
268
0
    cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(), "/Makefile");
269
270
0
  {
271
    // get a local generator for some useful methods
272
0
    auto& lg = cm::static_reference_cast<cmLocalUnixMakefileGenerator3>(
273
0
      this->LocalGenerators[0]);
274
275
    // Write the do not edit header.
276
0
    lg.WriteDisclaimer(cmakefileStream);
277
0
  }
278
279
  // Save the generator name
280
0
  cmakefileStream << "# The generator used is:\n"
281
0
                  << "set(CMAKE_DEPENDS_GENERATOR \"" << this->GetName()
282
0
                  << "\")\n\n";
283
284
  // for each cmMakefile get its list of dependencies
285
0
  std::vector<std::string> lfiles;
286
0
  for (auto const& localGen : this->LocalGenerators) {
287
    // Get the list of files contributing to this generation step.
288
0
    cm::append(lfiles, localGen->GetMakefile()->GetListFiles());
289
0
  }
290
291
0
  cmake* cm = this->GetCMakeInstance();
292
0
  if (cm->DoWriteGlobVerifyTarget()) {
293
0
    lfiles.push_back(cm->GetGlobVerifyScript());
294
0
    lfiles.push_back(cm->GetGlobVerifyStamp());
295
0
  }
296
297
  // Sort the list and remove duplicates.
298
0
  std::sort(lfiles.begin(), lfiles.end(), std::less<std::string>());
299
0
#if !defined(__VMS) // The Compaq STL on VMS crashes, so accept duplicates.
300
0
  auto new_end = std::unique(lfiles.begin(), lfiles.end());
301
0
  lfiles.erase(new_end, lfiles.end());
302
0
#endif
303
304
0
  {
305
    // reset lg to the first makefile
306
0
    auto const& lg = cm::static_reference_cast<cmLocalUnixMakefileGenerator3>(
307
0
      this->LocalGenerators[0]);
308
309
    // Save the list to the cmake file.
310
0
    cmakefileStream
311
0
      << "# The top level Makefile was generated from the following files:\n"
312
0
      << "set(CMAKE_MAKEFILE_DEPENDS\n"
313
0
      << "  \"CMakeCache.txt\"\n";
314
0
    for (std::string const& f : lfiles) {
315
0
      cmakefileStream << "  \"" << lg.MaybeRelativeToCurBinDir(f) << "\"\n";
316
0
    }
317
0
    cmakefileStream << "  )\n\n";
318
319
    // Build the path to the cache check file.
320
0
    std::string check =
321
0
      cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(),
322
0
               "/CMakeFiles/cmake.check_cache");
323
324
    // Set the corresponding makefile in the cmake file.
325
0
    cmakefileStream << "# The corresponding makefile is:\n"
326
0
                    << "set(CMAKE_MAKEFILE_OUTPUTS\n"
327
0
                    << "  \"" << lg.MaybeRelativeToCurBinDir(makefileName)
328
0
                    << "\"\n"
329
0
                    << "  \"" << lg.MaybeRelativeToCurBinDir(check) << "\"\n";
330
0
    cmakefileStream << "  )\n\n";
331
332
    // CMake must rerun if a byproduct is missing.
333
0
    cmakefileStream << "# Byproducts of CMake generate step:\n"
334
0
                    << "set(CMAKE_MAKEFILE_PRODUCTS\n";
335
336
    // add in any byproducts and all the directory information files
337
0
    std::string tmpStr;
338
0
    for (auto const& localGen : this->LocalGenerators) {
339
0
      for (std::string const& outfile :
340
0
           localGen->GetMakefile()->GetOutputFiles()) {
341
0
        cmakefileStream << "  \"" << lg.MaybeRelativeToTopBinDir(outfile)
342
0
                        << "\"\n";
343
0
      }
344
0
      tmpStr = cmStrCat(localGen->GetCurrentBinaryDirectory(),
345
0
                        "/CMakeFiles/CMakeDirectoryInformation.cmake");
346
0
      cmakefileStream << "  \"" << localGen->MaybeRelativeToTopBinDir(tmpStr)
347
0
                      << "\"\n";
348
0
    }
349
0
    cmakefileStream << "  )\n\n";
350
0
  }
351
352
0
  this->WriteMainCMakefileLanguageRules(cmakefileStream,
353
0
                                        this->LocalGenerators);
354
0
}
355
356
void cmGlobalUnixMakefileGenerator3::WriteMainCMakefileLanguageRules(
357
  cmGeneratedFileStream& cmakefileStream,
358
  std::vector<std::unique_ptr<cmLocalGenerator>>& lGenerators)
359
0
{
360
  // now list all the target info files
361
0
  cmakefileStream << "# Dependency information for all targets:\n";
362
0
  cmakefileStream << "set(CMAKE_DEPEND_INFO_FILES\n";
363
0
  for (auto const& lGenerator : lGenerators) {
364
0
    auto const& lg =
365
0
      cm::static_reference_cast<cmLocalUnixMakefileGenerator3>(lGenerator);
366
    // for all of out targets
367
0
    for (auto const& tgt : lg.GetGeneratorTargets()) {
368
0
      if (tgt->IsInBuildSystem() &&
369
0
          tgt->GetType() != cm::TargetType::GLOBAL_TARGET) {
370
0
        std::string tname = cmStrCat(lg.GetRelativeTargetDirectory(tgt.get()),
371
0
                                     "/DependInfo.cmake");
372
0
        cmSystemTools::ConvertToUnixSlashes(tname);
373
0
        cmakefileStream << "  \"" << tname << "\"\n";
374
0
      }
375
0
    }
376
0
  }
377
0
  cmakefileStream << "  )\n";
378
0
}
379
380
void cmGlobalUnixMakefileGenerator3::WriteDirectoryRule2(
381
  std::ostream& ruleFileStream, cmLocalUnixMakefileGenerator3& rootLG,
382
  DirectoryTarget const& dt, char const* pass, bool check_all,
383
  bool check_relink, std::vector<std::string> const& commands)
384
0
{
385
0
  auto* lg = static_cast<cmLocalUnixMakefileGenerator3*>(dt.LG);
386
0
  std::string makeTarget =
387
0
    cmStrCat(lg->GetCurrentBinaryDirectory(), '/', pass);
388
389
  // The directory-level rule should depend on the target-level rules
390
  // for all targets in the directory.
391
0
  std::vector<std::string> depends;
392
0
  for (DirectoryTarget::Target const& t : dt.Targets) {
393
    // Add this to the list of depends rules in this directory.
394
0
    if ((!check_all || t.ExcludedFromAllInConfigs.empty()) &&
395
0
        (!check_relink ||
396
0
         t.GT->NeedRelinkBeforeInstall(lg->GetConfigName()))) {
397
      // The target may be from a different directory; use its local gen.
398
0
      auto const* tlg = static_cast<cmLocalUnixMakefileGenerator3 const*>(
399
0
        t.GT->GetLocalGenerator());
400
0
      std::string tname =
401
0
        cmStrCat(tlg->GetRelativeTargetDirectory(t.GT), '/', pass);
402
0
      depends.push_back(std::move(tname));
403
0
    }
404
0
  }
405
406
  // The directory-level rule should depend on the directory-level
407
  // rules of the subdirectories.
408
0
  for (DirectoryTarget::Dir const& d : dt.Children) {
409
0
    if (check_all && d.ExcludeFromAll) {
410
0
      continue;
411
0
    }
412
0
    std::string subdir = cmStrCat(d.Path, '/', pass);
413
0
    depends.push_back(std::move(subdir));
414
0
  }
415
416
  // Work-around for makes that drop rules that have no dependencies
417
  // or commands.
418
0
  if (depends.empty() && !this->EmptyRuleHackDepends.empty()) {
419
0
    depends.push_back(this->EmptyRuleHackDepends);
420
0
  }
421
422
  // Write the rule.
423
0
  std::string doc;
424
0
  if (lg->IsRootMakefile()) {
425
0
    doc = cmStrCat("The main recursive \"", pass, "\" target.");
426
0
  } else {
427
0
    doc = cmStrCat("Recursive \"", pass, "\" directory target.");
428
0
  }
429
430
0
  rootLG.WriteMakeRule(ruleFileStream, doc.c_str(), makeTarget, depends,
431
0
                       commands, true);
432
0
}
433
434
void cmGlobalUnixMakefileGenerator3::WriteDirectoryRules2(
435
  std::ostream& ruleFileStream, cmLocalUnixMakefileGenerator3& rootLG,
436
  DirectoryTarget const& dt)
437
0
{
438
0
  auto* lg = static_cast<cmLocalUnixMakefileGenerator3*>(dt.LG);
439
  // Begin the directory-level rules section.
440
0
  {
441
0
    std::string dir = cmSystemTools::ConvertToOutputPath(
442
0
      rootLG.MaybeRelativeToTopBinDir(lg->GetCurrentBinaryDirectory()));
443
0
    rootLG.WriteDivider(ruleFileStream);
444
0
    if (lg->IsRootMakefile()) {
445
0
      ruleFileStream << "# Directory level rules for the build root directory";
446
0
    } else {
447
0
      ruleFileStream << "# Directory level rules for directory " << dir;
448
0
    }
449
0
    ruleFileStream << "\n\n";
450
0
  }
451
452
  // Write directory-level rules for "all".
453
0
  this->WriteDirectoryRule2(ruleFileStream, rootLG, dt, "all", true, false);
454
455
  // Write directory-level rules for "codegen".
456
0
  this->WriteDirectoryRule2(ruleFileStream, rootLG, dt, "codegen", true,
457
0
                            false);
458
459
  // Write directory-level rules for "preinstall".
460
0
  this->WriteDirectoryRule2(ruleFileStream, rootLG, dt, "preinstall", true,
461
0
                            true);
462
463
  // Write directory-level rules for "clean".
464
0
  {
465
0
    std::vector<std::string> cmds;
466
0
    lg->AppendDirectoryCleanCommand(cmds);
467
0
    this->WriteDirectoryRule2(ruleFileStream, rootLG, dt, "clean", false,
468
0
                              false, cmds);
469
0
  }
470
0
}
471
472
namespace {
473
std::string ConvertToMakefilePathForUnix(std::string const& path)
474
0
{
475
0
  std::string result;
476
0
  result.reserve(path.size());
477
0
  for (char c : path) {
478
0
    switch (c) {
479
0
      case '=':
480
        // We provide 'EQUALS = =' to encode '=' in a non-assignment case.
481
0
        result.append("$(EQUALS)");
482
0
        break;
483
0
      case '$':
484
0
        result.append("$$");
485
0
        break;
486
0
      case '\\':
487
0
      case ' ':
488
0
      case '#':
489
0
        result.push_back('\\');
490
0
        CM_FALLTHROUGH;
491
0
      default:
492
0
        result.push_back(c);
493
0
        break;
494
0
    }
495
0
  }
496
0
  return result;
497
0
}
498
499
#if defined(_WIN32) && !defined(__CYGWIN__)
500
std::string ConvertToMakefilePathForWindows(std::string const& path)
501
{
502
  bool const quote = path.find_first_of(" #") != std::string::npos;
503
  std::string result;
504
  result.reserve(path.size() + (quote ? 2 : 0));
505
  if (quote) {
506
    result.push_back('"');
507
  }
508
  for (char c : path) {
509
    switch (c) {
510
      case '=':
511
        // We provide 'EQUALS = =' to encode '=' in a non-assignment case.
512
        result.append("$(EQUALS)");
513
        break;
514
      case '$':
515
        result.append("$$");
516
        break;
517
      case '/':
518
        result.push_back('\\');
519
        break;
520
      default:
521
        result.push_back(c);
522
        break;
523
    }
524
  }
525
  if (quote) {
526
    result.push_back('"');
527
  }
528
  return result;
529
}
530
#endif
531
}
532
533
std::string cmGlobalUnixMakefileGenerator3::ConvertToMakefilePath(
534
  std::string const& path) const
535
0
{
536
#if defined(_WIN32) && !defined(__CYGWIN__)
537
  if (!this->ForceUnixPaths) {
538
    return ConvertToMakefilePathForWindows(path);
539
  }
540
#endif
541
0
  return ConvertToMakefilePathForUnix(path);
542
0
}
543
544
std::vector<cmGlobalGenerator::GeneratedMakeCommand>
545
cmGlobalUnixMakefileGenerator3::GenerateBuildCommand(
546
  std::string const& makeProgram, std::string const& /*projectName*/,
547
  std::string const& /*projectDir*/,
548
  std::vector<std::string> const& targetNames, std::string const& /*config*/,
549
  int jobs, bool verbose, cmBuildOptions buildOptions,
550
  std::vector<std::string> const& makeOptions,
551
  BuildTryCompile /*isInTryCompile*/)
552
0
{
553
0
  GeneratedMakeCommand makeCommand;
554
555
  // Make it possible to set verbosity also from command line
556
0
  if (verbose) {
557
0
    makeCommand.Add(cmSystemTools::GetCMakeCommand());
558
0
    makeCommand.Add("-E");
559
0
    makeCommand.Add("env");
560
0
    makeCommand.Add("VERBOSE=1");
561
0
  }
562
0
  makeCommand.Add(this->SelectMakeProgram(makeProgram));
563
564
  // Explicitly tell the make tool to use the Makefile written by
565
  // cmLocalUnixMakefileGenerator3::WriteLocalMakefile
566
0
  makeCommand.Add("-f");
567
0
  makeCommand.Add("Makefile");
568
569
0
  if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) {
570
0
    if (jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL) {
571
0
      makeCommand.Add("-j");
572
0
    } else {
573
0
      makeCommand.Add(cmStrCat("-j", jobs));
574
0
    }
575
0
  }
576
577
0
  makeCommand.Add(makeOptions.begin(), makeOptions.end());
578
0
  for (auto tname : targetNames) {
579
0
    if (!tname.empty()) {
580
0
      if (buildOptions.Fast) {
581
0
        tname += "/fast";
582
0
      }
583
0
      cmSystemTools::ConvertToOutputSlashes(tname);
584
0
      makeCommand.Add(std::move(tname));
585
0
    }
586
0
  }
587
0
  return { std::move(makeCommand) };
588
0
}
589
590
void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules(
591
  std::ostream& ruleFileStream, std::set<std::string>& emitted)
592
0
{
593
0
  std::vector<std::string> depends;
594
0
  std::vector<std::string> commands;
595
596
0
  bool regenerate = !this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION");
597
0
  if (regenerate) {
598
0
    depends.emplace_back("cmake_check_build_system");
599
0
  }
600
601
  // write the target convenience rules
602
0
  for (auto const& localGen : this->LocalGenerators) {
603
0
    auto& lg =
604
0
      cm::static_reference_cast<cmLocalUnixMakefileGenerator3>(localGen);
605
    // for each target Generate the rule files for each target.
606
0
    for (auto const& gtarget : lg.GetGeneratorTargets()) {
607
      // Don't emit the same rule twice (e.g. two targets with the same
608
      // simple name)
609
0
      std::string name = gtarget->GetName();
610
0
      if (!name.empty() && emitted.insert(name).second &&
611
          // Handle user targets here.  Global targets are handled in
612
          // the local generator on a per-directory basis.
613
0
          (gtarget->IsInBuildSystem() &&
614
0
           gtarget->GetType() != cm::TargetType::GLOBAL_TARGET)) {
615
        // Add a rule to build the target by name.
616
0
        lg.WriteDivider(ruleFileStream);
617
0
        ruleFileStream << "# Target rules for targets named " << name
618
0
                       << "\n\n";
619
620
        // Write the rule.
621
0
        commands.clear();
622
0
        std::string tmp = "CMakeFiles/Makefile2";
623
0
        commands.push_back(lg.GetRecursiveMakeCall(tmp, name));
624
0
        depends.clear();
625
0
        if (regenerate) {
626
0
          depends.emplace_back("cmake_check_build_system");
627
0
        }
628
0
        lg.WriteMakeRule(ruleFileStream, "Build rule for target.", name,
629
0
                         depends, commands, true);
630
631
        // Add a fast rule to build the target
632
0
        std::string localName = lg.GetRelativeTargetDirectory(gtarget.get());
633
0
        std::string makefileName;
634
0
        makefileName = cmStrCat(localName, "/build.make");
635
0
        depends.clear();
636
0
        commands.clear();
637
0
        std::string makeTargetName = cmStrCat(localName, "/build");
638
0
        localName = cmStrCat(name, "/fast");
639
0
        commands.push_back(
640
0
          lg.GetRecursiveMakeCall(makefileName, makeTargetName));
641
0
        lg.WriteMakeRule(ruleFileStream, "fast build rule for target.",
642
0
                         localName, depends, commands, true);
643
644
        // Add a local name for the rule to relink the target before
645
        // installation.
646
0
        if (gtarget->NeedRelinkBeforeInstall(lg.GetConfigName())) {
647
0
          makeTargetName = cmStrCat(
648
0
            lg.GetRelativeTargetDirectory(gtarget.get()), "/preinstall");
649
0
          localName = cmStrCat(name, "/preinstall");
650
0
          depends.clear();
651
0
          commands.clear();
652
0
          commands.push_back(
653
0
            lg.GetRecursiveMakeCall(makefileName, makeTargetName));
654
0
          lg.WriteMakeRule(ruleFileStream,
655
0
                           "Manual pre-install relink rule for target.",
656
0
                           localName, depends, commands, true);
657
0
        }
658
0
      }
659
0
    }
660
0
  }
661
662
  // Forward the test_prep/ convenience targets to CMakeFiles/Makefile2.
663
0
  this->WriteTestPrepConvenienceRules(ruleFileStream);
664
0
}
665
666
void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules2(
667
  std::ostream& ruleFileStream, cmLocalUnixMakefileGenerator3& rootLG,
668
  cmLocalUnixMakefileGenerator3& lg)
669
0
{
670
0
  std::vector<std::string> depends;
671
0
  std::vector<std::string> commands;
672
0
  std::string localName;
673
0
  std::string makeTargetName;
674
675
0
  bool regenerate = !this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION");
676
0
  if (regenerate) {
677
0
    depends.emplace_back("cmake_check_build_system");
678
0
  }
679
680
  // for each target Generate the rule files for each target.
681
0
  for (auto const& gtarget : lg.GetGeneratorTargets()) {
682
0
    std::string name = gtarget->GetName();
683
0
    if (!name.empty() &&
684
0
        (gtarget->IsInBuildSystem() &&
685
0
         gtarget->GetType() != cm::TargetType::GLOBAL_TARGET)) {
686
0
      std::string makefileName;
687
      // Add a rule to build the target by name.
688
0
      localName = lg.GetRelativeTargetDirectory(gtarget.get());
689
0
      makefileName = cmStrCat(localName, "/build.make");
690
691
0
      lg.WriteDivider(ruleFileStream);
692
0
      ruleFileStream << "# Target rules for target " << localName << "\n\n";
693
694
0
      commands.clear();
695
0
      makeTargetName = cmStrCat(localName, "/depend");
696
0
      commands.push_back(
697
0
        lg.GetRecursiveMakeCall(makefileName, makeTargetName));
698
699
0
      makeTargetName = cmStrCat(localName, "/build");
700
0
      commands.push_back(
701
0
        lg.GetRecursiveMakeCall(makefileName, makeTargetName));
702
703
      // Write the rule.
704
0
      localName += "/all";
705
0
      depends.clear();
706
707
0
      cmLocalUnixMakefileGenerator3::EchoProgress progress;
708
0
      progress.Dir = cmStrCat(lg.GetBinaryDirectory(), "/CMakeFiles");
709
0
      {
710
0
        progress.Arg = cmJoin(this->ProgressMap[gtarget.get()].Marks, ",");
711
0
      }
712
713
0
      bool targetMessages = true;
714
0
      if (cmValue tgtMsg =
715
0
            this->GetCMakeInstance()->GetState()->GetGlobalProperty(
716
0
              "TARGET_MESSAGES")) {
717
0
        targetMessages = tgtMsg.IsOn();
718
0
      }
719
720
0
      if (targetMessages) {
721
0
        lg.AppendEcho(commands, "Built target " + name,
722
0
                      cmLocalUnixMakefileGenerator3::EchoNormal, &progress);
723
0
      }
724
725
0
      this->AppendGlobalTargetDepends(depends, gtarget.get());
726
0
      rootLG.WriteMakeRule(ruleFileStream, "All Build rule for target.",
727
0
                           localName, depends, commands, true);
728
729
      // Write the rule.
730
0
      commands.clear();
731
732
0
      {
733
        // TODO: Convert the total progress count to a make variable.
734
0
        std::ostringstream progCmd;
735
0
        progCmd << "$(CMAKE_COMMAND) -E cmake_progress_start ";
736
        // # in target
737
0
        progCmd << lg.ConvertToOutputFormat(progress.Dir,
738
0
                                            cmOutputConverter::SHELL);
739
        //
740
0
        std::set<cmGeneratorTarget const*> emitted;
741
0
        progCmd << " "
742
0
                << this->CountProgressMarksInTarget(gtarget.get(), emitted);
743
0
        commands.push_back(progCmd.str());
744
0
      }
745
0
      std::string tmp = "CMakeFiles/Makefile2";
746
0
      commands.push_back(lg.GetRecursiveMakeCall(tmp, localName));
747
0
      {
748
0
        std::ostringstream progCmd;
749
0
        progCmd << "$(CMAKE_COMMAND) -E cmake_progress_start "; // # 0
750
0
        progCmd << lg.ConvertToOutputFormat(progress.Dir,
751
0
                                            cmOutputConverter::SHELL);
752
0
        progCmd << " 0";
753
0
        commands.push_back(progCmd.str());
754
0
      }
755
0
      depends.clear();
756
0
      if (regenerate) {
757
0
        depends.emplace_back("cmake_check_build_system");
758
0
      }
759
0
      localName =
760
0
        cmStrCat(lg.GetRelativeTargetDirectory(gtarget.get()), "/rule");
761
0
      rootLG.WriteMakeRule(ruleFileStream,
762
0
                           "Build rule for subdir invocation for target.",
763
0
                           localName, depends, commands, true);
764
765
      // Add a target with the canonical name (no prefix, suffix or path).
766
0
      commands.clear();
767
0
      depends.clear();
768
0
      depends.push_back(localName);
769
0
      rootLG.WriteMakeRule(ruleFileStream, "Convenience name for target.",
770
0
                           name, depends, commands, true);
771
772
      // Add rules to prepare the target for installation.
773
0
      if (gtarget->NeedRelinkBeforeInstall(lg.GetConfigName())) {
774
0
        localName = cmStrCat(lg.GetRelativeTargetDirectory(gtarget.get()),
775
0
                             "/preinstall");
776
0
        depends.clear();
777
0
        commands.clear();
778
0
        commands.push_back(lg.GetRecursiveMakeCall(makefileName, localName));
779
0
        rootLG.WriteMakeRule(ruleFileStream,
780
0
                             "Pre-install relink rule for target.", localName,
781
0
                             depends, commands, true);
782
0
      }
783
784
      // add the codegen rule
785
0
      localName = lg.GetRelativeTargetDirectory(gtarget.get());
786
0
      depends.clear();
787
0
      commands.clear();
788
0
      makeTargetName = cmStrCat(localName, "/codegen");
789
0
      commands.push_back(
790
0
        lg.GetRecursiveMakeCall(makefileName, makeTargetName));
791
0
      if (targetMessages) {
792
0
        lg.AppendEcho(commands, "Finished codegen for target " + name,
793
0
                      cmLocalUnixMakefileGenerator3::EchoNormal, &progress);
794
0
      }
795
0
      this->AppendCodegenTargetDepends(depends, gtarget.get());
796
0
      rootLG.WriteMakeRule(ruleFileStream, "codegen rule for target.",
797
0
                           makeTargetName, depends, commands, true);
798
799
      // add the clean rule
800
0
      localName = lg.GetRelativeTargetDirectory(gtarget.get());
801
0
      makeTargetName = cmStrCat(localName, "/clean");
802
0
      depends.clear();
803
0
      commands.clear();
804
0
      commands.push_back(
805
0
        lg.GetRecursiveMakeCall(makefileName, makeTargetName));
806
0
      rootLG.WriteMakeRule(ruleFileStream, "clean rule for target.",
807
0
                           makeTargetName, depends, commands, true);
808
0
      commands.clear();
809
0
    }
810
0
  }
811
0
}
812
813
// Build a map that contains the set of targets used by each local
814
// generator directory level.
815
void cmGlobalUnixMakefileGenerator3::InitializeProgressMarks()
816
0
{
817
0
  this->DirectoryTargetsMap.clear();
818
  // Loop over all targets in all local generators.
819
0
  for (auto const& lg : this->LocalGenerators) {
820
0
    for (auto const& gt : lg->GetGeneratorTargets()) {
821
0
      if (!gt->IsInBuildSystem() || this->IsExcluded(lg.get(), gt.get())) {
822
0
        continue;
823
0
      }
824
825
0
      cmStateSnapshot csnp = lg->GetStateSnapshot();
826
827
      // Consider the directory containing the target and all its parents.
828
      // An excluded directory may contains non-excluded targets.
829
0
      for (; csnp.IsValid(); csnp = csnp.GetBuildsystemDirectoryParent()) {
830
        // This local generator includes the target.
831
0
        std::set<cmGeneratorTarget const*>& targetSet =
832
0
          this->DirectoryTargetsMap[csnp];
833
0
        targetSet.insert(gt.get());
834
835
        // Add dependencies of the included target.  An excluded
836
        // target may still be included if it is a dependency of a
837
        // non-excluded target.
838
0
        for (cmTargetDepend const& tgtdep :
839
0
             this->GetTargetDirectDepends(gt.get())) {
840
0
          targetSet.insert(tgtdep);
841
0
        }
842
0
      }
843
0
    }
844
0
  }
845
0
}
846
847
size_t cmGlobalUnixMakefileGenerator3::CountProgressMarksInTarget(
848
  cmGeneratorTarget const* target, std::set<cmGeneratorTarget const*>& emitted)
849
0
{
850
0
  size_t count = 0;
851
0
  if (emitted.insert(target).second) {
852
0
    count = this->ProgressMap[target].Marks.size();
853
0
    for (cmTargetDepend const& depend : this->GetTargetDirectDepends(target)) {
854
0
      if (!depend->IsInBuildSystem()) {
855
0
        continue;
856
0
      }
857
0
      count += this->CountProgressMarksInTarget(depend, emitted);
858
0
    }
859
0
  }
860
0
  return count;
861
0
}
862
863
size_t cmGlobalUnixMakefileGenerator3::CountProgressMarksInAll(
864
  cmLocalGenerator const& lg)
865
0
{
866
0
  size_t count = 0;
867
0
  std::set<cmGeneratorTarget const*> emitted;
868
0
  for (cmGeneratorTarget const* target :
869
0
       this->DirectoryTargetsMap[lg.GetStateSnapshot()]) {
870
0
    if (!this->IsExcluded(&lg, target)) {
871
0
      count += this->CountProgressMarksInTarget(target, emitted);
872
0
    }
873
0
  }
874
0
  return count;
875
0
}
876
877
void cmGlobalUnixMakefileGenerator3::RecordTargetProgress(
878
  cmMakefileTargetGenerator* tg)
879
0
{
880
0
  TargetProgress& tp = this->ProgressMap[tg->GetGeneratorTarget()];
881
0
  tp.NumberOfActions = tg->GetNumberOfProgressActions();
882
0
  tp.VariableFile = tg->GetProgressFileNameFull();
883
0
}
884
885
void cmGlobalUnixMakefileGenerator3::TargetProgress::WriteProgressVariables(
886
  unsigned long total, unsigned long& current)
887
0
{
888
0
  cmGeneratedFileStream fout(this->VariableFile);
889
0
  for (unsigned long i = 1; i <= this->NumberOfActions; ++i) {
890
0
    fout << "CMAKE_PROGRESS_" << i << " = ";
891
0
    if (total <= 100) {
892
0
      unsigned long num = i + current;
893
0
      fout << num;
894
0
      this->Marks.push_back(num);
895
0
    } else if (((i + current) * 100) / total >
896
0
               ((i - 1 + current) * 100) / total) {
897
0
      unsigned long num = ((i + current) * 100) / total;
898
0
      fout << num;
899
0
      this->Marks.push_back(num);
900
0
    }
901
0
    fout << "\n";
902
0
  }
903
0
  fout << "\n";
904
0
  current += this->NumberOfActions;
905
0
}
906
907
void cmGlobalUnixMakefileGenerator3::AppendGlobalTargetDepends(
908
  std::vector<std::string>& depends, cmGeneratorTarget* target)
909
0
{
910
0
  for (cmTargetDepend const& i : this->GetTargetDirectDepends(target)) {
911
    // Create the target-level dependency.
912
0
    cmGeneratorTarget const* dep = i;
913
0
    if (!dep->IsInBuildSystem()) {
914
0
      continue;
915
0
    }
916
0
    cmLocalUnixMakefileGenerator3* lg3 =
917
0
      static_cast<cmLocalUnixMakefileGenerator3*>(dep->GetLocalGenerator());
918
0
    std::string tgtName = cmStrCat(
919
0
      lg3->GetRelativeTargetDirectory(const_cast<cmGeneratorTarget*>(dep)),
920
0
      "/all");
921
0
    depends.push_back(tgtName);
922
0
  }
923
0
}
924
925
void cmGlobalUnixMakefileGenerator3::AppendCodegenTargetDepends(
926
  std::vector<std::string>& depends, cmGeneratorTarget* target)
927
0
{
928
0
  std::set<std::string> const& codegen_depends =
929
0
    target->Target->GetCodegenDeps();
930
931
0
  for (cmTargetDepend const& i : this->GetTargetDirectDepends(target)) {
932
    // Create the target-level dependency.
933
0
    cmGeneratorTarget const* dep = i;
934
0
    if (!dep->IsInBuildSystem()) {
935
0
      continue;
936
0
    }
937
0
    if (codegen_depends.find(dep->GetName()) != codegen_depends.end()) {
938
0
      cmLocalUnixMakefileGenerator3* lg3 =
939
0
        static_cast<cmLocalUnixMakefileGenerator3*>(dep->GetLocalGenerator());
940
0
      std::string tgtName = cmStrCat(
941
0
        lg3->GetRelativeTargetDirectory(const_cast<cmGeneratorTarget*>(dep)),
942
0
        "/all");
943
0
      depends.push_back(tgtName);
944
0
    }
945
0
  }
946
0
}
947
948
void cmGlobalUnixMakefileGenerator3::ComputeTestPrepTargets()
949
0
{
950
0
  this->TestPrepTargets.clear();
951
0
  this->TestPrepEnabled = false;
952
0
  if (this->Makefiles.empty() ||
953
0
      !this->Makefiles.front()->IsOn("CMAKE_TEST_BUILD_DEPENDS")) {
954
0
    return;
955
0
  }
956
0
  this->TestPrepEnabled = true;
957
958
  // Map a generator target to its "<target>.dir/all" recursive rule.
959
0
  auto targetAllRule = [](cmGeneratorTarget* gt) -> std::string {
960
0
    auto* lg3 =
961
0
      static_cast<cmLocalUnixMakefileGenerator3*>(gt->GetLocalGenerator());
962
0
    return cmStrCat(lg3->GetRelativeTargetDirectory(gt), "/all");
963
0
  };
964
965
  // Collect the dependencies of each test, merging tests that share a name
966
  // across directories (as the Ninja generator does).
967
0
  for (auto const& lg : this->LocalGenerators) {
968
0
    for (auto const& tester : lg->GetMakefile()->GetTestGenerators()) {
969
0
      cmTestGenerator::BuildDependencies deps;
970
0
      if (!tester->GetBuildDependencies(lg.get(), deps)) {
971
0
        continue;
972
0
      }
973
0
      cmTest* test = tester->GetTest();
974
0
      std::string const& testName = test->GetName();
975
976
      // A Makefile target name cannot contain ':'.  Such a name is valid for
977
      // add_test() (e.g. a namespaced name) and works with the Ninja
978
      // generator, but cannot be expressed as a Makefile rule.
979
0
      if (testName.find(':') != std::string::npos) {
980
0
        test->GetMakefile()->IssueMessage(
981
0
          MessageType::WARNING,
982
0
          cmStrCat("Test \"", testName,
983
0
                   "\" has a name containing ':', which cannot be used as a "
984
0
                   "Makefile build target.  No \"test_prep/\" target will be "
985
0
                   "generated for it.  Use the Ninja generator or rename the "
986
0
                   "test to build its dependencies with a \"test_prep/\" "
987
0
                   "target."),
988
0
          test->GetBacktrace());
989
0
        continue;
990
0
      }
991
992
0
      std::vector<std::string>& rules =
993
0
        this->TestPrepTargets[cmStrCat("test_prep/", testName)];
994
995
      // Target dependencies are filtered to build-system targets by
996
      // GetBuildDependencies.
997
0
      for (cmGeneratorTarget* dep : deps.Targets) {
998
0
        rules.push_back(targetAllRule(dep));
999
0
      }
1000
1001
0
      for (cmTestGenerator::BuildDependencies::FileDependency const& file :
1002
0
           deps.Files) {
1003
0
        if (file.Owner) {
1004
          // The file is the primary output of one build-system target; build
1005
          // that target to produce the file.
1006
0
          rules.push_back(targetAllRule(file.Owner));
1007
0
        } else if (file.Generated) {
1008
          // The file is generated but cannot be attributed to a single owning
1009
          // target (e.g. a byproduct or an ambiguous/shared output), so the
1010
          // recursive Makefile graph cannot build it from a top-level rule.
1011
0
          test->GetMakefile()->IssueMessage(
1012
0
            MessageType::WARNING,
1013
0
            cmStrCat("Test \"", testName, "\" BUILD_DEPENDS file\n  ",
1014
0
                     file.Path,
1015
0
                     "\nis generated but is not the unique output of a build "
1016
0
                     "target, so the \"test_prep/",
1017
0
                     testName,
1018
0
                     "\" target cannot build it with this generator.  Depend "
1019
0
                     "on the target that produces it (for example one created "
1020
0
                     "with add_custom_target) instead."),
1021
0
            test->GetBacktrace());
1022
0
        }
1023
        // Otherwise the file is not generated by the build (e.g. a source
1024
        // file that already exists) and needs no build rule.
1025
0
      }
1026
0
    }
1027
0
  }
1028
1029
  // Sort and de-duplicate each rule list (as the Ninja generator does).
1030
0
  for (auto& entry : this->TestPrepTargets) {
1031
0
    std::vector<std::string>& rules = entry.second;
1032
0
    std::sort(rules.begin(), rules.end());
1033
0
    rules.erase(std::unique(rules.begin(), rules.end()), rules.end());
1034
0
  }
1035
0
}
1036
1037
void cmGlobalUnixMakefileGenerator3::WriteTestPrepRules(
1038
  std::ostream& makefileStream, cmLocalUnixMakefileGenerator3& rootLG)
1039
0
{
1040
0
  if (!this->TestPrepEnabled) {
1041
0
    return;
1042
0
  }
1043
1044
0
  rootLG.WriteDivider(makefileStream);
1045
0
  makefileStream << "# Targets to build the dependencies of tests.\n\n";
1046
1047
0
  std::vector<std::string> no_commands;
1048
0
  std::vector<std::string> allDeps;
1049
0
  for (auto const& entry : this->TestPrepTargets) {
1050
0
    std::vector<std::string> depends = entry.second;
1051
0
    if (depends.empty() && !this->EmptyRuleHackDepends.empty()) {
1052
0
      depends.push_back(this->EmptyRuleHackDepends);
1053
0
    }
1054
0
    rootLG.WriteMakeRule(makefileStream, "Build the dependencies of a test.",
1055
0
                         entry.first, depends, no_commands, true);
1056
0
    allDeps.push_back(entry.first);
1057
0
  }
1058
1059
0
  if (allDeps.empty() && !this->EmptyRuleHackDepends.empty()) {
1060
0
    allDeps.push_back(this->EmptyRuleHackDepends);
1061
0
  }
1062
0
  rootLG.WriteMakeRule(makefileStream, "Build the dependencies of all tests.",
1063
0
                       "test_prep/all", allDeps, no_commands, true);
1064
0
}
1065
1066
void cmGlobalUnixMakefileGenerator3::WriteTestPrepConvenienceRules(
1067
  std::ostream& ruleFileStream)
1068
0
{
1069
0
  if (!this->TestPrepEnabled) {
1070
0
    return;
1071
0
  }
1072
1073
0
  auto& lg = cm::static_reference_cast<cmLocalUnixMakefileGenerator3>(
1074
0
    this->LocalGenerators[0]);
1075
1076
0
  bool regenerate = !this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION");
1077
0
  std::string const makefile2 = "CMakeFiles/Makefile2";
1078
1079
0
  std::vector<std::string> depends;
1080
0
  std::vector<std::string> commands;
1081
0
  auto writeForward = [&](std::string const& prepName) {
1082
0
    depends.clear();
1083
0
    if (regenerate) {
1084
0
      depends.emplace_back("cmake_check_build_system");
1085
0
    }
1086
0
    commands.clear();
1087
0
    commands.push_back(lg.GetRecursiveMakeCall(makefile2, prepName));
1088
0
    lg.WriteMakeRule(ruleFileStream, "Build the dependencies of a test.",
1089
0
                     prepName, depends, commands, true);
1090
0
  };
1091
1092
0
  lg.WriteDivider(ruleFileStream);
1093
0
  ruleFileStream << "# Convenience rules to build test dependencies.\n\n";
1094
1095
0
  for (auto const& entry : this->TestPrepTargets) {
1096
0
    writeForward(entry.first);
1097
0
  }
1098
0
  writeForward("test_prep/all");
1099
0
}
1100
1101
void cmGlobalUnixMakefileGenerator3::WriteHelpRule(
1102
  std::ostream& ruleFileStream, cmLocalUnixMakefileGenerator3* lg)
1103
0
{
1104
  // add the help target
1105
0
  std::string path;
1106
0
  std::vector<std::string> no_depends;
1107
0
  std::vector<std::string> commands;
1108
0
  lg->AppendEcho(commands,
1109
0
                 "The following are some of the valid targets "
1110
0
                 "for this Makefile:");
1111
0
  lg->AppendEcho(commands, "... all (the default if no target is provided)");
1112
0
  lg->AppendEcho(commands, "... clean");
1113
0
  if (!this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) {
1114
0
    lg->AppendEcho(commands, "... depend");
1115
0
  }
1116
0
  if (this->CheckCMP0171()) {
1117
0
    lg->AppendEcho(commands, "... codegen");
1118
0
  }
1119
1120
  // Keep track of targets already listed.
1121
0
  std::set<std::string> emittedTargets;
1122
0
  std::set<std::string> utility_targets;
1123
0
  std::set<std::string> globals_targets;
1124
0
  std::set<std::string> project_targets;
1125
1126
  // for each local generator
1127
0
  for (auto const& localGen : this->LocalGenerators) {
1128
0
    auto const& lg2 =
1129
0
      cm::static_reference_cast<cmLocalUnixMakefileGenerator3>(localGen);
1130
    // for the passed in makefile or if this is the top Makefile wripte out
1131
    // the targets
1132
0
    if (&lg2 == lg || lg->IsRootMakefile()) {
1133
      // for each target Generate the rule files for each target.
1134
0
      for (auto const& target : lg2.GetGeneratorTargets()) {
1135
0
        cm::TargetType type = target->GetType();
1136
0
        if ((type == cm::TargetType::EXECUTABLE) ||
1137
0
            (type == cm::TargetType::STATIC_LIBRARY) ||
1138
0
            (type == cm::TargetType::SHARED_LIBRARY) ||
1139
0
            (type == cm::TargetType::MODULE_LIBRARY) ||
1140
0
            (type == cm::TargetType::OBJECT_LIBRARY) ||
1141
0
            (type == cm::TargetType::INTERFACE_LIBRARY &&
1142
0
             target->IsInBuildSystem())) {
1143
0
          project_targets.insert(target->GetName());
1144
0
        } else if (type == cm::TargetType::GLOBAL_TARGET) {
1145
0
          globals_targets.insert(target->GetName());
1146
0
        } else if (type == cm::TargetType::UTILITY) {
1147
0
          utility_targets.insert(target->GetName());
1148
0
        }
1149
0
      }
1150
0
    }
1151
0
  }
1152
1153
0
  for (std::string const& name : globals_targets) {
1154
0
    path = cmStrCat("... ", name);
1155
0
    lg->AppendEcho(commands, path);
1156
0
  }
1157
0
  for (std::string const& name : utility_targets) {
1158
0
    path = cmStrCat("... ", name);
1159
0
    lg->AppendEcho(commands, path);
1160
0
  }
1161
0
  for (std::string const& name : project_targets) {
1162
0
    path = cmStrCat("... ", name);
1163
0
    lg->AppendEcho(commands, path);
1164
0
  }
1165
1166
0
  for (std::string const& o : lg->GetLocalHelp()) {
1167
0
    path = cmStrCat("... ", o);
1168
0
    lg->AppendEcho(commands, path);
1169
0
  }
1170
0
  lg->WriteMakeRule(ruleFileStream, "Help Target", "help", no_depends,
1171
0
                    commands, true);
1172
0
  ruleFileStream << "\n\n";
1173
0
}