Coverage Report

Created: 2026-02-09 06:05

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