Coverage Report

Created: 2026-09-14 06:43

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