Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmLocalUnixMakefileGenerator3.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 "cmLocalUnixMakefileGenerator3.h"
4
5
#include <algorithm>
6
#include <cassert>
7
#include <cstdio>
8
#include <functional>
9
#include <iostream>
10
#include <utility>
11
12
#include <cm/memory>
13
#include <cm/optional>
14
#include <cm/string_view>
15
#include <cm/vector>
16
#include <cmext/algorithm>
17
#include <cmext/string_view>
18
19
#include "cmsys/FStream.hxx"
20
21
#include "cmCMakePath.h"
22
#include "cmCustomCommand.h" // IWYU pragma: keep
23
#include "cmCustomCommandGenerator.h"
24
#include "cmDependsCompiler.h"
25
#include "cmFileTimeCache.h"
26
#include "cmGeneratedFileStream.h"
27
#include "cmGeneratorExpression.h"
28
#include "cmGeneratorTarget.h"
29
#include "cmGlobalGenerator.h"
30
#include "cmGlobalUnixMakefileGenerator3.h"
31
#include "cmInstrumentation.h"
32
#include "cmInstrumentationQuery.h"
33
#include "cmList.h"
34
#include "cmListFileCache.h"
35
#include "cmLocalGenerator.h"
36
#include "cmMakefile.h"
37
#include "cmMakefileTargetGenerator.h"
38
#include "cmOutputConverter.h"
39
#include "cmRange.h"
40
#include "cmRulePlaceholderExpander.h"
41
#include "cmScriptGenerator.h"
42
#include "cmState.h"
43
#include "cmStateSnapshot.h"
44
#include "cmStdIoStream.h"
45
#include "cmStdIoTerminal.h"
46
#include "cmStringAlgorithms.h"
47
#include "cmSystemTools.h"
48
#include "cmTargetDepend.h"
49
#include "cmTargetTypes.h"
50
#include "cmValue.h"
51
#include "cmVersion.h"
52
#include "cmake.h"
53
54
// Include dependency scanners for supported languages.  Only the
55
// C/C++ scanner is needed for bootstrapping CMake.
56
#include "cmDependsC.h"
57
#ifndef CMAKE_BOOTSTRAP
58
#  include "cmDependsFortran.h"
59
#  include "cmDependsJava.h"
60
#endif
61
62
namespace {
63
// Helper function used below.
64
std::string cmSplitExtension(std::string const& in, std::string& base)
65
0
{
66
0
  std::string ext;
67
0
  std::string::size_type dot_pos = in.rfind('.');
68
0
  if (dot_pos != std::string::npos) {
69
    // Remove the extension first in case &base == &in.
70
0
    ext = in.substr(dot_pos);
71
0
    base = in.substr(0, dot_pos);
72
0
  } else {
73
0
    base = in;
74
0
  }
75
0
  return ext;
76
0
}
77
78
#ifndef CMAKE_BOOTSTRAP
79
// Helper function to add the Start Instrumentation command
80
void addInstrumentationCommand(cmInstrumentation* instrumentation,
81
                               std::vector<std::string>& commands)
82
0
{
83
0
  if (instrumentation->HasQuery()) {
84
0
    std::string instrumentationCommand =
85
0
      "$(CTEST_COMMAND) --start-instrumentation $(CMAKE_BINARY_DIR)";
86
0
#  ifndef _WIN32
87
    /*
88
     * On Unix systems, Make will prefix the command with `/bin/sh -c`.
89
     * Use exec so that Make is the parent process of the command.
90
     * Add a `;` to convince BSD make to not optimize out the shell.
91
     */
92
0
    instrumentationCommand =
93
0
      cmStrCat("exec ", std::move(instrumentationCommand), " ;");
94
0
#  endif
95
0
    commands.push_back(instrumentationCommand);
96
0
  }
97
0
}
98
#endif
99
100
// Helper predicate for removing absolute paths that don't point to the
101
// source or binary directory. It is used when CMAKE_DEPENDS_IN_PROJECT_ONLY
102
// is set ON, to only consider in-project dependencies during the build.
103
class NotInProjectDir
104
{
105
public:
106
  // Constructor with the source and binary directory's path
107
  NotInProjectDir(cm::string_view sourceDir, cm::string_view binaryDir)
108
0
    : SourceDir(sourceDir)
109
0
    , BinaryDir(binaryDir)
110
0
  {
111
0
  }
112
113
  // Operator evaluating the predicate
114
  bool operator()(std::string const& p) const
115
0
  {
116
0
    auto path = cmCMakePath(p).Normal();
117
118
    // Keep all relative paths:
119
0
    if (path.IsRelative()) {
120
0
      return false;
121
0
    }
122
123
    // If it's an absolute path, check if it starts with the source
124
    // directory:
125
0
    return !(cmCMakePath(this->SourceDir).IsPrefix(path) ||
126
0
             cmCMakePath(this->BinaryDir).IsPrefix(path));
127
0
  }
128
129
private:
130
  // The path to the source directory
131
  cm::string_view SourceDir;
132
  // The path to the binary directory
133
  cm::string_view BinaryDir;
134
};
135
}
136
137
cmLocalUnixMakefileGenerator3::cmLocalUnixMakefileGenerator3(
138
  cmGlobalGenerator* gg, cmMakefile* mf)
139
0
  : cmLocalCommonGenerator(gg, mf)
140
0
{
141
0
  this->MakefileVariableSize = 0;
142
0
  this->ColorMakefile = false;
143
0
  this->SkipPreprocessedSourceRules = false;
144
0
  this->SkipAssemblySourceRules = false;
145
0
  this->MakeCommandEscapeTargetTwice = false;
146
0
  this->BorlandMakeCurlyHack = false;
147
0
}
148
149
0
cmLocalUnixMakefileGenerator3::~cmLocalUnixMakefileGenerator3() = default;
150
151
std::string const& cmLocalUnixMakefileGenerator3::GetConfigName() const
152
0
{
153
0
  auto const& configNames = this->GetConfigNames();
154
0
  assert(configNames.size() == 1);
155
0
  return configNames.front();
156
0
}
157
158
void cmLocalUnixMakefileGenerator3::Generate()
159
0
{
160
  // Record whether some options are enabled to avoid checking many
161
  // times later.
162
0
  if (!this->GetGlobalGenerator()->GetCMakeInstance()->GetIsInTryCompile()) {
163
0
    if (this->Makefile->IsSet("CMAKE_COLOR_MAKEFILE")) {
164
0
      this->ColorMakefile = this->Makefile->IsOn("CMAKE_COLOR_MAKEFILE");
165
0
    } else {
166
0
      this->ColorMakefile = this->Makefile->IsOn("CMAKE_COLOR_DIAGNOSTICS");
167
0
    }
168
0
  }
169
0
  this->SkipPreprocessedSourceRules =
170
0
    this->Makefile->IsOn("CMAKE_SKIP_PREPROCESSED_SOURCE_RULES");
171
0
  this->SkipAssemblySourceRules =
172
0
    this->Makefile->IsOn("CMAKE_SKIP_ASSEMBLY_SOURCE_RULES");
173
174
  // Generate the rule files for each target.
175
0
  cmGlobalUnixMakefileGenerator3* gg =
176
0
    static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
177
0
  for (cmGeneratorTarget* gt :
178
0
       this->GlobalGenerator->GetLocalGeneratorTargetsInOrder(this)) {
179
0
    if (!gt->IsInBuildSystem()) {
180
0
      continue;
181
0
    }
182
183
0
    auto& gtVisited = this->GetCommandsVisited(gt);
184
0
    auto const& deps = this->GlobalGenerator->GetTargetDirectDepends(gt);
185
0
    for (auto const& d : deps) {
186
      // Take the union of visited source files of custom commands
187
0
      auto depVisited = this->GetCommandsVisited(d);
188
0
      gtVisited.insert(depVisited.begin(), depVisited.end());
189
0
    }
190
191
0
    std::unique_ptr<cmMakefileTargetGenerator> tg(
192
0
      cmMakefileTargetGenerator::New(gt));
193
0
    if (tg) {
194
0
      tg->WriteRuleFiles();
195
0
      gg->RecordTargetProgress(tg.get());
196
0
    }
197
0
  }
198
199
  // write the local Makefile
200
0
  this->WriteLocalMakefile();
201
202
  // Write the cmake file with information for this directory.
203
0
  this->WriteDirectoryInformationFile();
204
0
}
205
206
std::string cmLocalUnixMakefileGenerator3::GetObjectOutputRoot(
207
  cmStateEnums::IntermediateDirKind kind) const
208
0
{
209
0
  if (this->UseShortObjectNames(kind)) {
210
0
    return cmStrCat(this->GetCurrentBinaryDirectory(), '/',
211
0
                    this->GetGlobalGenerator()->GetShortBinaryOutputDir());
212
0
  }
213
0
  return cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles");
214
0
}
215
216
void cmLocalUnixMakefileGenerator3::ComputeHomeRelativeOutputPath()
217
0
{
218
  // Compute the path to use when referencing the current output
219
  // directory from the top output directory.
220
0
  this->HomeRelativeOutputPath =
221
0
    this->MaybeRelativeToTopBinDir(this->GetCurrentBinaryDirectory());
222
0
  if (this->HomeRelativeOutputPath == ".") {
223
0
    this->HomeRelativeOutputPath.clear();
224
0
  }
225
0
  if (!this->HomeRelativeOutputPath.empty()) {
226
0
    this->HomeRelativeOutputPath += "/";
227
0
  }
228
0
}
229
230
void cmLocalUnixMakefileGenerator3::GetLocalObjectFiles(
231
  std::map<std::string, LocalObjectInfo>& localObjectFiles)
232
0
{
233
0
  for (auto const& gt : this->GetGeneratorTargets()) {
234
0
    if (!gt->CanCompileSources()) {
235
0
      continue;
236
0
    }
237
0
    std::vector<cmSourceFile const*> objectSources;
238
0
    gt->GetObjectSources(objectSources, this->GetConfigName());
239
    // Compute full path to object file directory for this target.
240
0
    std::string dir = cmStrCat(gt->GetSupportDirectory(), '/');
241
    // Compute the name of each object file.
242
0
    for (cmSourceFile const* sf : objectSources) {
243
0
      bool hasSourceExtension = true;
244
0
      std::string objectName =
245
0
        this->GetObjectFileNameWithoutTarget(*sf, dir, &hasSourceExtension);
246
0
      if (cmSystemTools::FileIsFullPath(objectName)) {
247
0
        objectName = cmSystemTools::GetFilenameName(objectName);
248
0
      }
249
0
      LocalObjectInfo& info = localObjectFiles[objectName];
250
0
      info.HasSourceExtension = hasSourceExtension;
251
0
      info.emplace_back(gt.get(),
252
0
                        gt->GetSourceFileLanguage(sf, this->GetConfigName()));
253
0
    }
254
0
  }
255
0
}
256
257
void cmLocalUnixMakefileGenerator3::GetIndividualFileTargets(
258
  std::vector<std::string>& targets)
259
0
{
260
0
  std::map<std::string, LocalObjectInfo> localObjectFiles;
261
0
  this->GetLocalObjectFiles(localObjectFiles);
262
0
  for (auto const& localObjectFile : localObjectFiles) {
263
0
    targets.push_back(localObjectFile.first);
264
265
0
    std::string::size_type dot_pos = localObjectFile.first.rfind('.');
266
0
    std::string base = localObjectFile.first.substr(0, dot_pos);
267
0
    if (localObjectFile.second.HasPreprocessRule) {
268
0
      targets.push_back(base + ".i");
269
0
    }
270
271
0
    if (localObjectFile.second.HasAssembleRule) {
272
0
      targets.push_back(base + ".s");
273
0
    }
274
0
  }
275
0
}
276
277
std::string cmLocalUnixMakefileGenerator3::GetLinkDependencyFile(
278
  cmGeneratorTarget* target, std::string const& /*config*/) const
279
0
{
280
0
  return cmStrCat(target->GetSupportDirectory(), "/link.d");
281
0
}
282
283
void cmLocalUnixMakefileGenerator3::WriteLocalMakefile()
284
0
{
285
  // generate the includes
286
0
  std::string ruleFileName = "Makefile";
287
288
  // Open the rule file.  This should be copy-if-different because the
289
  // rules may depend on this file itself.
290
0
  std::string ruleFileNameFull = this->ConvertToFullPath(ruleFileName);
291
0
  cmGeneratedFileStream ruleFileStream(
292
0
    ruleFileNameFull, false, this->GlobalGenerator->GetMakefileEncoding());
293
0
  if (!ruleFileStream) {
294
0
    return;
295
0
  }
296
  // always write the top makefile
297
0
  if (!this->IsRootMakefile()) {
298
0
    ruleFileStream.SetCopyIfDifferent(true);
299
0
  }
300
301
  // write the all rules
302
0
  this->WriteLocalAllRules(ruleFileStream);
303
304
  // only write local targets unless at the top Keep track of targets already
305
  // listed.
306
0
  std::set<std::string> emittedTargets;
307
0
  if (!this->IsRootMakefile()) {
308
    // write our targets, and while doing it collect up the object
309
    // file rules
310
0
    this->WriteLocalMakefileTargets(ruleFileStream, emittedTargets);
311
0
  } else {
312
0
    cmGlobalUnixMakefileGenerator3* gg =
313
0
      static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
314
0
    gg->WriteConvenienceRules(ruleFileStream, emittedTargets);
315
0
  }
316
317
0
  bool do_preprocess_rules = this->GetCreatePreprocessedSourceRules();
318
0
  bool do_assembly_rules = this->GetCreateAssemblySourceRules();
319
320
0
  std::map<std::string, LocalObjectInfo> localObjectFiles;
321
0
  this->GetLocalObjectFiles(localObjectFiles);
322
323
  // now write out the object rules
324
  // for each object file name
325
0
  for (auto& localObjectFile : localObjectFiles) {
326
    // Add a convenience rule for building the object file.
327
0
    this->WriteObjectConvenienceRule(
328
0
      ruleFileStream, "target to build an object file", localObjectFile.first,
329
0
      localObjectFile.second);
330
331
    // Check whether preprocessing and assembly rules make sense.
332
0
    bool lang_has_preprocessor = false;
333
0
    bool lang_has_assembly = false;
334
335
0
    for (LocalObjectEntry const& entry : localObjectFile.second) {
336
0
      if (entry.Language == "C" || entry.Language == "CXX" ||
337
0
          entry.Language == "CUDA" || entry.Language == "Fortran" ||
338
0
          entry.Language == "HIP" || entry.Language == "ISPC" ||
339
0
          entry.Language == "SYCL") {
340
        // These languages have both a preprocessor and the ability to generate
341
        // assembly code.
342
0
        lang_has_preprocessor = true;
343
0
        lang_has_assembly = true;
344
0
        break;
345
0
      }
346
0
    }
347
348
    // Add convenience rules for preprocessed and assembly files.
349
0
    if (lang_has_preprocessor && do_preprocess_rules) {
350
0
      std::string::size_type dot_pos = localObjectFile.first.rfind(".");
351
0
      std::string base = localObjectFile.first.substr(0, dot_pos);
352
0
      this->WriteObjectConvenienceRule(ruleFileStream,
353
0
                                       "target to preprocess a source file",
354
0
                                       (base + ".i"), localObjectFile.second);
355
0
      localObjectFile.second.HasPreprocessRule = true;
356
0
    }
357
358
0
    if (lang_has_assembly && do_assembly_rules) {
359
0
      std::string::size_type dot_pos = localObjectFile.first.rfind(".");
360
0
      std::string base = localObjectFile.first.substr(0, dot_pos);
361
0
      this->WriteObjectConvenienceRule(
362
0
        ruleFileStream, "target to generate assembly for a file",
363
0
        (base + ".s"), localObjectFile.second);
364
0
      localObjectFile.second.HasAssembleRule = true;
365
0
    }
366
0
  }
367
368
  // add a help target as long as there isn;t a real target named help
369
0
  if (emittedTargets.insert("help").second) {
370
0
    cmGlobalUnixMakefileGenerator3* gg =
371
0
      static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
372
0
    gg->WriteHelpRule(ruleFileStream, this);
373
0
  }
374
375
0
  this->WriteSpecialTargetsBottom(ruleFileStream);
376
0
}
377
378
void cmLocalUnixMakefileGenerator3::WriteObjectConvenienceRule(
379
  std::ostream& ruleFileStream, char const* comment, std::string const& output,
380
  LocalObjectInfo const& info)
381
0
{
382
  // If the rule includes the source file extension then create a
383
  // version that has the extension removed.  The help should include
384
  // only the version without source extension.
385
0
  bool inHelp = true;
386
0
  if (info.HasSourceExtension) {
387
    // Remove the last extension.  This should be kept.
388
0
    std::string outBase1 = output;
389
0
    std::string outExt1 = cmSplitExtension(outBase1, outBase1);
390
391
    // Now remove the source extension and put back the last
392
    // extension.
393
0
    std::string outNoExt;
394
0
    cmSplitExtension(outBase1, outNoExt);
395
0
    outNoExt += outExt1;
396
397
    // Add a rule to drive the rule below.
398
0
    std::vector<std::string> depends;
399
0
    depends.emplace_back(output);
400
0
    std::vector<std::string> no_commands;
401
0
    this->WriteMakeRule(ruleFileStream, nullptr, outNoExt, depends,
402
0
                        no_commands, true, true);
403
0
    inHelp = false;
404
0
  }
405
406
  // Recursively make the rule for each target using the object file.
407
0
  std::vector<std::string> commands;
408
0
  for (LocalObjectEntry const& t : info) {
409
0
    std::string tgtMakefileName = this->GetRelativeTargetDirectory(t.Target);
410
0
    std::string targetName = tgtMakefileName;
411
0
    tgtMakefileName += "/build.make";
412
0
    targetName += "/";
413
0
    targetName += output;
414
0
    commands.push_back(
415
0
      this->GetRecursiveMakeCall(tgtMakefileName, targetName));
416
0
  }
417
0
  this->CreateCDCommand(commands, this->GetBinaryDirectory(),
418
0
                        this->GetCurrentBinaryDirectory());
419
420
  // Write the rule to the makefile.
421
0
  std::vector<std::string> no_depends;
422
0
  this->WriteMakeRule(ruleFileStream, comment, output, no_depends, commands,
423
0
                      true, inHelp);
424
0
}
425
426
void cmLocalUnixMakefileGenerator3::WriteLocalMakefileTargets(
427
  std::ostream& ruleFileStream, std::set<std::string>& emitted)
428
0
{
429
0
  std::vector<std::string> depends;
430
0
  std::vector<std::string> commands;
431
432
  // for each target we just provide a rule to cd up to the top and do a make
433
  // on the target
434
0
  std::string localName;
435
0
  for (auto const& target : this->GetGeneratorTargets()) {
436
0
    if ((target->GetType() == cm::TargetType::EXECUTABLE) ||
437
0
        (target->GetType() == cm::TargetType::STATIC_LIBRARY) ||
438
0
        (target->GetType() == cm::TargetType::SHARED_LIBRARY) ||
439
0
        (target->GetType() == cm::TargetType::MODULE_LIBRARY) ||
440
0
        (target->GetType() == cm::TargetType::OBJECT_LIBRARY) ||
441
0
        (target->GetType() == cm::TargetType::UTILITY)) {
442
0
      emitted.insert(target->GetName());
443
444
      // for subdirs add a rule to build this specific target by name.
445
0
      localName =
446
0
        cmStrCat(this->GetRelativeTargetDirectory(target.get()), "/rule");
447
0
      commands.clear();
448
0
      depends.clear();
449
450
      // Build the target for this pass.
451
0
      std::string makefile2 = "CMakeFiles/Makefile2";
452
0
      commands.push_back(this->GetRecursiveMakeCall(makefile2, localName));
453
0
      this->CreateCDCommand(commands, this->GetBinaryDirectory(),
454
0
                            this->GetCurrentBinaryDirectory());
455
0
      this->WriteMakeRule(ruleFileStream, "Convenience name for target.",
456
0
                          localName, depends, commands, true);
457
458
      // Add a target with the canonical name (no prefix, suffix or path).
459
0
      if (localName != target->GetName()) {
460
0
        commands.clear();
461
0
        depends.push_back(localName);
462
0
        this->WriteMakeRule(ruleFileStream, "Convenience name for target.",
463
0
                            target->GetName(), depends, commands, true);
464
0
      }
465
466
      // Add a fast rule to build the target
467
0
      std::string makefileName = cmStrCat(
468
0
        this->GetRelativeTargetDirectory(target.get()), "/build.make");
469
      // make sure the makefile name is suitable for a makefile
470
0
      std::string makeTargetName =
471
0
        cmStrCat(this->GetRelativeTargetDirectory(target.get()), "/build");
472
0
      localName = cmStrCat(target->GetName(), "/fast");
473
0
      depends.clear();
474
0
      commands.clear();
475
0
      commands.push_back(
476
0
        this->GetRecursiveMakeCall(makefileName, makeTargetName));
477
0
      this->CreateCDCommand(commands, this->GetBinaryDirectory(),
478
0
                            this->GetCurrentBinaryDirectory());
479
0
      this->WriteMakeRule(ruleFileStream, "fast build rule for target.",
480
0
                          localName, depends, commands, true);
481
482
      // Add a local name for the rule to relink the target before
483
      // installation.
484
0
      if (target->NeedRelinkBeforeInstall(this->GetConfigName())) {
485
0
        makeTargetName = cmStrCat(
486
0
          this->GetRelativeTargetDirectory(target.get()), "/preinstall");
487
0
        localName = cmStrCat(target->GetName(), "/preinstall");
488
0
        depends.clear();
489
0
        commands.clear();
490
0
        commands.push_back(
491
0
          this->GetRecursiveMakeCall(makefile2, makeTargetName));
492
0
        this->CreateCDCommand(commands, this->GetBinaryDirectory(),
493
0
                              this->GetCurrentBinaryDirectory());
494
0
        this->WriteMakeRule(ruleFileStream,
495
0
                            "Manual pre-install relink rule for target.",
496
0
                            localName, depends, commands, true);
497
0
      }
498
0
    }
499
0
  }
500
0
}
501
502
void cmLocalUnixMakefileGenerator3::WriteDirectoryInformationFile()
503
0
{
504
0
  std::string infoFileName =
505
0
    cmStrCat(this->GetCurrentBinaryDirectory(),
506
0
             "/CMakeFiles/CMakeDirectoryInformation.cmake");
507
508
  // Open the output file.
509
0
  cmGeneratedFileStream infoFileStream(infoFileName);
510
0
  if (!infoFileStream) {
511
0
    return;
512
0
  }
513
514
0
  infoFileStream.SetCopyIfDifferent(true);
515
  // Write the do not edit header.
516
0
  this->WriteDisclaimer(infoFileStream);
517
518
  // Setup relative path conversion tops.
519
0
  infoFileStream << "# Relative path conversion top directories.\n"
520
0
                    "set(CMAKE_RELATIVE_PATH_TOP_SOURCE \""
521
0
                 << this->GetRelativePathTopSource() << "\")\n"
522
0
                 << "set(CMAKE_RELATIVE_PATH_TOP_BINARY \""
523
0
                 << this->GetRelativePathTopBinary() << "\")\n"
524
0
                 << '\n';
525
526
  // Tell the dependency scanner to use unix paths if necessary.
527
0
  if (cmSystemTools::GetForceUnixPaths()) {
528
0
    infoFileStream << "# Force unix paths in dependencies.\n"
529
0
                      "set(CMAKE_FORCE_UNIX_PATHS 1)\n"
530
0
                      "\n";
531
0
  }
532
533
  // Store the include regular expressions for this directory.
534
0
  infoFileStream << "\n"
535
0
                    "# The C and CXX include file regular expressions for "
536
0
                    "this directory.\n"
537
0
                    "set(CMAKE_C_INCLUDE_REGEX_SCAN ";
538
0
  cmLocalUnixMakefileGenerator3::WriteCMakeArgument(
539
0
    infoFileStream, this->Makefile->GetIncludeRegularExpression());
540
0
  infoFileStream << ")\n"
541
0
                    "set(CMAKE_C_INCLUDE_REGEX_COMPLAIN ";
542
0
  cmLocalUnixMakefileGenerator3::WriteCMakeArgument(
543
0
    infoFileStream, this->Makefile->GetComplainRegularExpression());
544
0
  infoFileStream
545
0
    << ")\n"
546
0
    << "set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN})\n"
547
0
       "set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN "
548
0
       "${CMAKE_C_INCLUDE_REGEX_COMPLAIN})\n";
549
0
}
550
551
std::string cmLocalUnixMakefileGenerator3::ConvertToFullPath(
552
  std::string const& localPath)
553
0
{
554
0
  std::string dir =
555
0
    cmStrCat(this->GetCurrentBinaryDirectory(), '/', localPath);
556
0
  return dir;
557
0
}
558
559
std::string const& cmLocalUnixMakefileGenerator3::GetHomeRelativeOutputPath()
560
0
{
561
0
  return this->HomeRelativeOutputPath;
562
0
}
563
564
std::string cmLocalUnixMakefileGenerator3::ConvertToMakefilePath(
565
  std::string const& path) const
566
0
{
567
0
  cmGlobalUnixMakefileGenerator3* gg =
568
0
    static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
569
0
  return gg->ConvertToMakefilePath(path);
570
0
}
571
572
void cmLocalUnixMakefileGenerator3::WriteMakeRule(
573
  std::ostream& os, char const* comment, std::string const& target,
574
  std::vector<std::string> const& depends,
575
  std::vector<std::string> const& commands, bool symbolic, bool in_help)
576
0
{
577
  // Make sure there is a target.
578
0
  if (target.empty()) {
579
0
    std::string err("No target for WriteMakeRule! called with comment: ");
580
0
    if (comment) {
581
0
      err += comment;
582
0
    }
583
0
    cmSystemTools::Error(err);
584
0
    return;
585
0
  }
586
587
0
  std::string replace;
588
589
  // Write the comment describing the rule in the makefile.
590
0
  if (comment) {
591
0
    replace = comment;
592
0
    std::string::size_type lpos = 0;
593
0
    std::string::size_type rpos;
594
0
    while ((rpos = replace.find('\n', lpos)) != std::string::npos) {
595
0
      os << "# " << replace.substr(lpos, rpos - lpos) << "\n";
596
0
      lpos = rpos + 1;
597
0
    }
598
0
    os << "# " << replace.substr(lpos) << "\n";
599
0
  }
600
601
  // Construct the left hand side of the rule.
602
0
  std::string tgt =
603
0
    this->ConvertToMakefilePath(this->MaybeRelativeToTopBinDir(target));
604
605
0
  char const* space = "";
606
0
  if (tgt.size() == 1) {
607
    // Add a space before the ":" to avoid drive letter confusion on
608
    // Windows.
609
0
    space = " ";
610
0
  }
611
612
  // Mark the rule as symbolic if requested.
613
0
  if (symbolic) {
614
0
    if (cmValue sym =
615
0
          this->Makefile->GetDefinition("CMAKE_MAKE_SYMBOLIC_RULE")) {
616
0
      os << tgt << space << ": " << *sym << '\n';
617
0
    }
618
0
  }
619
620
  // Write the rule.
621
0
  if (depends.empty()) {
622
    // No dependencies.  The commands will always run.
623
0
    os << tgt << space << ":\n";
624
0
  } else {
625
    // Split dependencies into multiple rule lines.  This allows for
626
    // very long dependency lists even on older make implementations.
627
0
    for (std::string const& depend : depends) {
628
0
      os << tgt << space << ": "
629
0
         << this->ConvertToMakefilePath(this->MaybeRelativeToTopBinDir(depend))
630
0
         << '\n';
631
0
    }
632
0
  }
633
634
0
  if (!commands.empty()) {
635
    // Write the list of commands.
636
0
    bool protectProcessMetrics = false;
637
0
#if !defined(_WIN32) && !defined(CMAKE_BOOTSTRAP)
638
0
    if (this->GetCMakeInstance()->GetInstrumentation()->HasOption(
639
0
          cmInstrumentationQuery::Option::ProcessMetrics)) {
640
      // Prevent shell exec optimization from passing accumulated child
641
      // resource usage to the final instrumentation wrapper.
642
0
      protectProcessMetrics = true;
643
0
    }
644
0
#endif
645
0
    for (std::string const& command : commands) {
646
0
      os << '\t' << command;
647
      // Preserve the instrumentation daemon's explicit exec command.
648
0
      if (protectProcessMetrics && !command.empty() &&
649
0
          !cmHasLiteralPrefix(command, "exec ")) {
650
0
        os << " && :";
651
0
      }
652
0
      os << '\n';
653
0
    }
654
0
  }
655
0
  if (symbolic && !this->IsWatcomWMake()) {
656
0
    os << ".PHONY : " << tgt << '\n';
657
0
  }
658
0
  os << '\n';
659
  // Add the output to the local help if requested.
660
0
  if (in_help) {
661
0
    this->LocalHelp.push_back(target);
662
0
  }
663
0
}
664
665
std::string cmLocalUnixMakefileGenerator3::MaybeConvertWatcomShellCommand(
666
  std::string const& cmd)
667
0
{
668
0
  if (this->IsWatcomWMake() && cmSystemTools::FileIsFullPath(cmd) &&
669
0
      cmd.find_first_of("( )") != std::string::npos) {
670
    // On Watcom WMake use the windows short path for the command
671
    // name.  This is needed to avoid funny quoting problems on
672
    // lines with shell redirection operators.
673
0
    std::string scmd;
674
0
    if (cmSystemTools::GetShortPath(cmd, scmd)) {
675
0
      return this->ConvertToOutputFormat(scmd, cmOutputConverter::SHELL);
676
0
    }
677
0
  }
678
0
  return std::string();
679
0
}
680
681
void cmLocalUnixMakefileGenerator3::WriteMakeVariables(
682
  std::ostream& makefileStream)
683
0
{
684
0
  this->WriteDivider(makefileStream);
685
0
  makefileStream << "# Set environment variables for the build.\n"
686
0
                    "\n";
687
0
  cmGlobalUnixMakefileGenerator3* gg =
688
0
    static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
689
0
  if (gg->DefineWindowsNULL) {
690
0
    makefileStream << "!IF \"$(OS)\" == \"Windows_NT\"\n"
691
0
                      "NULL=\n"
692
0
                      "!ELSE\n"
693
0
                      "NULL=nul\n"
694
0
                      "!ENDIF\n";
695
0
  }
696
0
  if (gg->IsGNUMakeJobServerAware()) {
697
    // Toggle for USES_TERMINAL recipes: empty here, set to "+" by
698
    // "cmake --build" under --output-sync (see AppendCustomCommand).
699
0
    makefileStream << "# Prefix for USES_TERMINAL recipes under output sync.\n"
700
0
                      "CMAKE_USES_TERMINAL_PREFIX =\n"
701
0
                      "\n";
702
0
  }
703
0
  if (this->IsWindowsShell()) {
704
0
    makefileStream << "SHELL = cmd.exe\n"
705
0
                      "\n";
706
0
  } else {
707
0
#if !defined(__VMS)
708
0
    makefileStream << "# The shell in which to execute make rules.\n"
709
0
                      "SHELL = /bin/sh\n"
710
0
                      "\n";
711
0
#endif
712
0
  }
713
714
0
  auto getShellCommand = [this](std::string command) -> std::string {
715
0
    std::string shellCommand = this->MaybeConvertWatcomShellCommand(command);
716
0
    return shellCommand.empty()
717
0
      ? this->ConvertToOutputFormat(command, cmOutputConverter::SHELL)
718
0
      : shellCommand;
719
0
  };
720
721
0
  std::string cmakeShellCommand =
722
0
    getShellCommand(cmSystemTools::GetCMakeCommand());
723
724
0
  makefileStream << "# The CMake executable.\n"
725
0
                    "CMAKE_COMMAND = "
726
0
                 << cmakeShellCommand << "\n";
727
728
0
#ifndef CMAKE_BOOTSTRAP
729
0
  if (this->GetCMakeInstance()->GetInstrumentation()->HasQuery() &&
730
      // FIXME(#27079): This does not work for MSYS Makefiles.
731
0
      this->GlobalGenerator->GetName() != "MSYS Makefiles") {
732
0
    std::string ctestShellCommand =
733
0
      getShellCommand(cmSystemTools::GetCTestCommand());
734
0
    makefileStream << "# The CTest executable.\n"
735
0
                      "CTEST_COMMAND = "
736
0
                   << ctestShellCommand << "\n";
737
0
  }
738
0
#endif
739
740
0
  makefileStream
741
0
    << "\n"
742
0
       "# The command to remove a file.\n"
743
0
       "RM = "
744
0
    << cmakeShellCommand
745
0
    << " -E rm -f\n"
746
0
       "\n"
747
0
       "# Escaping for special characters.\n"
748
0
       "EQUALS = =\n"
749
0
       "\n"
750
0
       "# The top-level source directory on which CMake was run.\n"
751
0
       "CMAKE_SOURCE_DIR = "
752
0
    << this->ConvertToOutputFormat(this->GetSourceDirectory(),
753
0
                                   cmOutputConverter::SHELL)
754
0
    << "\n"
755
0
       "\n"
756
0
       "# The top-level build directory on which CMake was run.\n"
757
0
       "CMAKE_BINARY_DIR = "
758
0
    << this->ConvertToOutputFormat(this->GetBinaryDirectory(),
759
0
                                   cmOutputConverter::SHELL)
760
0
    << "\n"
761
0
       "\n";
762
0
}
763
764
void cmLocalUnixMakefileGenerator3::WriteSpecialTargetsTop(
765
  std::ostream& makefileStream)
766
0
{
767
0
  this->WriteDivider(makefileStream);
768
0
  makefileStream << "# Special targets provided by cmake.\n"
769
0
                    "\n";
770
771
0
  std::vector<std::string> no_commands;
772
0
  std::vector<std::string> no_depends;
773
774
  // Special target to cleanup operation of make tool.
775
  // This should be the first target except for the default_target in
776
  // the interface Makefile.
777
0
  this->WriteMakeRule(makefileStream,
778
0
                      "Disable implicit rules so canonical targets will work.",
779
0
                      ".SUFFIXES", no_depends, no_commands, false);
780
781
0
  if (!this->IsNMake() && !this->IsWatcomWMake() &&
782
0
      !this->BorlandMakeCurlyHack) {
783
    // turn off RCS and SCCS automatic stuff from gmake
784
0
    constexpr char const* vcs_rules[] = {
785
0
      "%,v", "RCS/%", "RCS/%,v", "SCCS/s.%", "s.%",
786
0
    };
787
0
    for (auto const* vcs_rule : vcs_rules) {
788
0
      std::vector<std::string> vcs_depend;
789
0
      vcs_depend.emplace_back(vcs_rule);
790
0
      this->WriteMakeRule(makefileStream, "Disable VCS-based implicit rules.",
791
0
                          "%", vcs_depend, no_commands, false);
792
0
    }
793
0
  }
794
  // Add a fake suffix to keep HP happy.  Must be max 32 chars for SGI make.
795
0
  std::vector<std::string> depends;
796
0
  depends.emplace_back(".hpux_make_needs_suffix_list");
797
0
  this->WriteMakeRule(makefileStream, nullptr, ".SUFFIXES", depends,
798
0
                      no_commands, false);
799
0
  if (this->IsWatcomWMake()) {
800
    // Switch on WMake feature, if an error or interrupt occurs during
801
    // makefile processing, the current target being made may be deleted
802
    // without prompting (the same as command line -e option).
803
0
    makefileStream << "\n"
804
0
                      ".ERASE\n"
805
0
                      "\n";
806
0
  }
807
0
  if (this->Makefile->IsOn("CMAKE_VERBOSE_MAKEFILE")) {
808
0
    makefileStream << "# Produce verbose output by default.\n"
809
0
                      "VERBOSE = 1\n"
810
0
                      "\n";
811
0
  }
812
0
  if (this->IsWatcomWMake()) {
813
0
    makefileStream << "!ifndef VERBOSE\n"
814
0
                      ".SILENT\n"
815
0
                      "!endif\n"
816
0
                      "\n";
817
0
  } else {
818
0
    makefileStream << "# Command-line flag to silence nested $(MAKE).\n"
819
0
                      "$(VERBOSE)MAKESILENT = -s\n"
820
0
                      "\n";
821
822
    // Write special target to silence make output.  This must be after
823
    // the default target in case VERBOSE is set (which changes the
824
    // name).  The setting of CMAKE_VERBOSE_MAKEFILE to ON will cause a
825
    // "VERBOSE=1" to be added as a make variable which will change the
826
    // name of this special target.  This gives a make-time choice to
827
    // the user.
828
    // Write directly to the stream since WriteMakeRule escapes '$'.
829
0
    makefileStream << "#Suppress display of executed commands.\n"
830
0
                      "$(VERBOSE).SILENT:\n"
831
0
                      "\n";
832
0
  }
833
834
  // Work-around for makes that drop rules that have no dependencies
835
  // or commands.
836
0
  cmGlobalUnixMakefileGenerator3* gg =
837
0
    static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
838
0
  std::string hack = gg->GetEmptyRuleHackDepends();
839
0
  if (!hack.empty()) {
840
0
    no_depends.push_back(std::move(hack));
841
0
  }
842
0
  std::string hack_cmd = gg->GetEmptyRuleHackCommand();
843
0
  if (!hack_cmd.empty()) {
844
0
    no_commands.push_back(std::move(hack_cmd));
845
0
  }
846
847
  // Special symbolic target that never exists to force dependers to
848
  // run their rules.
849
0
  this->WriteMakeRule(makefileStream, "A target that is always out of date.",
850
0
                      "cmake_force", no_depends, no_commands, true);
851
852
  // Variables for reference by other rules.
853
0
  this->WriteMakeVariables(makefileStream);
854
0
}
855
856
void cmLocalUnixMakefileGenerator3::WriteSpecialTargetsBottom(
857
  std::ostream& makefileStream)
858
0
{
859
0
  this->WriteDivider(makefileStream);
860
0
  makefileStream << "# Special targets to cleanup operation of make.\n"
861
0
                    "\n";
862
863
  // Write special "cmake_check_build_system" target to run cmake with
864
  // the --check-build-system flag.
865
0
  if (!this->GlobalGenerator->GlobalSettingIsOn(
866
0
        "CMAKE_SUPPRESS_REGENERATION")) {
867
    // Build command to run CMake to check if anything needs regenerating.
868
0
    std::vector<std::string> commands;
869
0
    cmake* cm = this->GlobalGenerator->GetCMakeInstance();
870
0
    if (cm->DoWriteGlobVerifyTarget()) {
871
0
      std::string rescanRule =
872
0
        cmStrCat("$(CMAKE_COMMAND) -P ",
873
0
                 this->ConvertToOutputFormat(cm->GetGlobVerifyScript(),
874
0
                                             cmOutputConverter::SHELL));
875
0
      commands.push_back(rescanRule);
876
0
    }
877
0
    std::string cmakefileName = "CMakeFiles/Makefile.cmake";
878
0
    std::string runRule = cmStrCat(
879
0
      "$(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) ",
880
0
      cm->GetIgnoreCompileWarningAsError() ? "--compile-no-warning-as-error "
881
0
                                           : "",
882
0
      cm->GetIgnoreLinkWarningAsError() ? "--link-no-warning-as-error " : "",
883
0
      "--check-build-system ",
884
0
      this->ConvertToOutputFormat(cmakefileName, cmOutputConverter::SHELL),
885
0
      " 0");
886
887
0
    std::vector<std::string> no_depends;
888
0
    commands.push_back(std::move(runRule));
889
0
#ifndef CMAKE_BOOTSTRAP
890
    // FIXME(#27079): This does not work for MSYS Makefiles.
891
0
    if (this->GlobalGenerator->GetName() != "MSYS Makefiles") {
892
0
      addInstrumentationCommand(this->GetCMakeInstance()->GetInstrumentation(),
893
0
                                commands);
894
0
    }
895
0
#endif
896
0
    if (!this->IsRootMakefile()) {
897
0
      this->CreateCDCommand(commands, this->GetBinaryDirectory(),
898
0
                            this->GetCurrentBinaryDirectory());
899
0
    }
900
0
    this->WriteMakeRule(makefileStream,
901
0
                        "Special rule to run CMake to check the build system "
902
0
                        "integrity.\n"
903
0
                        "No rule that depends on this can have "
904
0
                        "commands that come from listfiles\n"
905
0
                        "because they might be regenerated.",
906
0
                        "cmake_check_build_system", no_depends, commands,
907
0
                        true);
908
0
  }
909
0
}
910
911
void cmLocalUnixMakefileGenerator3::WriteConvenienceRule(
912
  std::ostream& ruleFileStream, std::string const& realTarget,
913
  std::string const& helpTarget)
914
0
{
915
  // A rule is only needed if the names are different.
916
0
  if (realTarget != helpTarget) {
917
    // The helper target depends on the real target.
918
0
    std::vector<std::string> depends;
919
0
    depends.push_back(realTarget);
920
921
    // There are no commands.
922
0
    std::vector<std::string> no_commands;
923
924
    // Write the rule.
925
0
    this->WriteMakeRule(ruleFileStream, "Convenience name for target.",
926
0
                        helpTarget, depends, no_commands, true);
927
0
  }
928
0
}
929
930
std::string cmLocalUnixMakefileGenerator3::GetRelativeTargetDirectory(
931
  cmGeneratorTarget const* target) const
932
0
{
933
0
  return this->MaybeRelativeToTopBinDir(target->GetSupportDirectory());
934
0
}
935
936
void cmLocalUnixMakefileGenerator3::AppendFlags(
937
  std::string& flags, std::string const& newFlags) const
938
0
{
939
0
  if (this->IsWatcomWMake() && !newFlags.empty()) {
940
0
    std::string newf = newFlags;
941
0
    if (newf.find("\\\"") != std::string::npos) {
942
0
      cmSystemTools::ReplaceString(newf, "\\\"", "\"");
943
0
      this->cmLocalGenerator::AppendFlags(flags, newf);
944
0
      return;
945
0
    }
946
0
  }
947
0
  this->cmLocalGenerator::AppendFlags(flags, newFlags);
948
0
}
949
950
void cmLocalUnixMakefileGenerator3::AppendRuleDepend(
951
  std::vector<std::string>& depends, char const* ruleFileName)
952
0
{
953
  // Add a dependency on the rule file itself unless an option to skip
954
  // it is specifically enabled by the user or project.
955
0
  cmValue nodep = this->Makefile->GetDefinition("CMAKE_SKIP_RULE_DEPENDENCY");
956
0
  if (nodep.IsOff()) {
957
0
    depends.emplace_back(ruleFileName);
958
0
  }
959
0
}
960
961
void cmLocalUnixMakefileGenerator3::AppendRuleDepends(
962
  std::vector<std::string>& depends, std::vector<std::string> const& ruleFiles)
963
0
{
964
  // Add a dependency on the rule file itself unless an option to skip
965
  // it is specifically enabled by the user or project.
966
0
  if (!this->Makefile->IsOn("CMAKE_SKIP_RULE_DEPENDENCY")) {
967
0
    cm::append(depends, ruleFiles);
968
0
  }
969
0
}
970
971
void cmLocalUnixMakefileGenerator3::AppendCustomDepends(
972
  std::vector<std::string>& depends, std::vector<cmCustomCommand> const& ccs)
973
0
{
974
0
  for (cmCustomCommand const& cc : ccs) {
975
0
    cmCustomCommandGenerator ccg(cc, this->GetConfigName(), this);
976
0
    this->AppendCustomDepend(depends, ccg);
977
0
  }
978
0
}
979
980
void cmLocalUnixMakefileGenerator3::AppendCustomDepend(
981
  std::vector<std::string>& depends, cmCustomCommandGenerator const& ccg)
982
0
{
983
0
  for (std::string const& d : ccg.GetDepends()) {
984
    // Lookup the real name of the dependency in case it is a CMake target.
985
0
    std::string dep;
986
0
    if (this->GetRealDependency(d, this->GetConfigName(), dep,
987
0
                                ccg.GetCC().GetCMP0212Status())) {
988
0
      depends.push_back(std::move(dep));
989
0
    }
990
0
  }
991
0
}
992
993
void cmLocalUnixMakefileGenerator3::AppendCustomCommands(
994
  std::vector<std::string>& commands, std::vector<cmCustomCommand> const& ccs,
995
  cmGeneratorTarget* target, std::string const& relative)
996
0
{
997
0
  for (cmCustomCommand const& cc : ccs) {
998
0
    cmCustomCommandGenerator ccg(cc, this->GetConfigName(), this);
999
0
    this->AppendCustomCommand(commands, ccg, target, relative, true);
1000
0
  }
1001
0
}
1002
1003
void cmLocalUnixMakefileGenerator3::AppendCustomCommand(
1004
  std::vector<std::string>& commands, cmCustomCommandGenerator const& ccg,
1005
  cmGeneratorTarget* target, std::string const& relative, bool echo_comment,
1006
  std::ostream* content)
1007
0
{
1008
  // Optionally create a command to display the custom command's
1009
  // comment text.  This is used for pre-build, pre-link, and
1010
  // post-build command comments.  Custom build step commands have
1011
  // their comments generated elsewhere.
1012
0
  if (echo_comment) {
1013
0
    if (cm::optional<std::string> comment = ccg.GetComment()) {
1014
0
      this->AppendEcho(commands, *comment,
1015
0
                       cmLocalUnixMakefileGenerator3::EchoGenerate);
1016
0
    }
1017
0
  }
1018
1019
  // if the command specified a working directory use it.
1020
0
  std::string dir = this->GetCurrentBinaryDirectory();
1021
0
  std::string workingDir = ccg.GetWorkingDirectory();
1022
0
  if (!workingDir.empty()) {
1023
0
    dir = workingDir;
1024
0
  }
1025
0
  if (content) {
1026
0
    *content << dir;
1027
0
  }
1028
1029
0
  auto rulePlaceholderExpander = this->CreateRulePlaceholderExpander();
1030
1031
  // Add each command line to the set of commands.
1032
0
  std::vector<std::string> commands1;
1033
0
  for (unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) {
1034
    // Build the command line in a single string.
1035
0
    std::string cmd = ccg.GetCommand(c);
1036
0
    if (!cmd.empty()) {
1037
      // Use "call " before any invocations of .bat or .cmd files
1038
      // invoked as custom commands in the WindowsShell.
1039
      //
1040
0
      bool useCall = false;
1041
1042
0
      if (this->IsWindowsShell()) {
1043
0
        std::string suffix;
1044
0
        if (cmd.size() > 4) {
1045
0
          suffix = cmSystemTools::LowerCase(cmd.substr(cmd.size() - 4));
1046
0
          if (suffix == ".bat" || suffix == ".cmd") {
1047
0
            useCall = true;
1048
0
          }
1049
0
        }
1050
0
      }
1051
1052
0
      cmSystemTools::ReplaceString(cmd, "/./", "/");
1053
      // Convert the command to a relative path only if the current
1054
      // working directory will be the start-output directory.
1055
0
      bool had_slash = cmd.find('/') != std::string::npos;
1056
0
      if (workingDir.empty()) {
1057
0
        cmd = this->MaybeRelativeToCurBinDir(cmd);
1058
0
      }
1059
0
      bool has_slash = cmd.find('/') != std::string::npos;
1060
0
      if (had_slash && !has_slash) {
1061
        // This command was specified as a path to a file in the
1062
        // current directory.  Add a leading "./" so it can run
1063
        // without the current directory being in the search path.
1064
0
        cmd = cmStrCat("./", std::move(cmd));
1065
0
      }
1066
1067
0
      std::string launcher;
1068
      // Short-circuit if there is no launcher.
1069
0
      std::string val = this->GetRuleLauncher(
1070
0
        target, "RULE_LAUNCH_CUSTOM",
1071
0
        this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
1072
0
      if (cmNonempty(val)) {
1073
        // Expand rule variables referenced in the given launcher command.
1074
0
        cmRulePlaceholderExpander::RuleVariables vars;
1075
0
        std::string targetSupportDir =
1076
0
          target->GetGlobalGenerator()->ConvertToOutputPath(
1077
0
            target->GetCMFSupportDirectory());
1078
0
        targetSupportDir = target->GetLocalGenerator()->ConvertToOutputFormat(
1079
0
          target->GetLocalGenerator()->MaybeRelativeToTopBinDir(
1080
0
            targetSupportDir),
1081
0
          cmOutputConverter::SHELL);
1082
0
        vars.TargetSupportDir = targetSupportDir.c_str();
1083
0
        vars.CMTargetName = target->GetName().c_str();
1084
0
        vars.CMTargetType =
1085
0
          cmState::GetTargetTypeName(target->GetType()).c_str();
1086
0
        std::string output;
1087
0
        std::vector<std::string> const& outputs = ccg.GetOutputs();
1088
0
        for (std::string const& ccgOut : outputs) {
1089
0
          output = cmStrCat(std::move(output),
1090
0
                            this->ConvertToOutputFormat(
1091
0
                              ccg.GetWorkingDirectory().empty()
1092
0
                                ? this->MaybeRelativeToCurBinDir(ccgOut)
1093
0
                                : ccgOut,
1094
0
                              cmOutputConverter::SHELL),
1095
0
                            ',');
1096
0
        }
1097
0
        if (!output.empty()) {
1098
0
          output.pop_back();
1099
0
        }
1100
0
        vars.Output = output.c_str();
1101
1102
0
        std::string filePathWithOutput = ccg.StoreContentToFile(output);
1103
0
        vars.FilePathWithOutput = filePathWithOutput.c_str();
1104
1105
0
        vars.Role = ccg.GetCC().GetRole().c_str();
1106
0
        vars.CMTargetName = ccg.GetCC().GetTarget().c_str();
1107
0
        vars.Config = ccg.GetOutputConfig().c_str();
1108
1109
0
        launcher = val;
1110
0
        rulePlaceholderExpander->ExpandRuleVariables(this, launcher, vars);
1111
0
        if (!launcher.empty()) {
1112
0
          launcher += " ";
1113
0
        }
1114
0
      }
1115
1116
0
      std::string shellCommand = this->MaybeConvertWatcomShellCommand(cmd);
1117
0
      if (shellCommand.empty()) {
1118
0
        shellCommand =
1119
0
          this->ConvertToOutputFormat(cmd, cmOutputConverter::SHELL);
1120
0
      }
1121
0
      cmd = launcher + shellCommand;
1122
1123
0
      ccg.AppendArguments(c, cmd);
1124
0
      if (content) {
1125
        // Rule content does not include the launcher.
1126
0
        *content << (cmd.c_str() + launcher.size());
1127
0
      }
1128
0
      if (this->BorlandMakeCurlyHack) {
1129
        // Borland Make has a very strange bug.  If the first curly
1130
        // brace anywhere in the command string is a left curly, it
1131
        // must be written {{} instead of just {.  Otherwise some
1132
        // curly braces are removed.  The hack can be skipped if the
1133
        // first curly brace is the last character.
1134
0
        std::string::size_type lcurly = cmd.find('{');
1135
0
        if (lcurly != std::string::npos && lcurly < (cmd.size() - 1)) {
1136
0
          std::string::size_type rcurly = cmd.find('}');
1137
0
          if (rcurly == std::string::npos || rcurly > lcurly) {
1138
            // The first curly is a left curly.  Use the hack.
1139
0
            cmd =
1140
0
              cmStrCat(cmd.substr(0, lcurly), "{{}", cmd.substr(lcurly + 1));
1141
0
          }
1142
0
        }
1143
0
      }
1144
0
      if (launcher.empty()) {
1145
0
        if (useCall) {
1146
0
          cmd = cmStrCat("call ", std::move(cmd));
1147
0
        } else if (this->IsNMake() && cmd[0] == '"') {
1148
0
          cmd = cmStrCat("echo >nul && ", std::move(cmd));
1149
0
        }
1150
0
      }
1151
0
      commands1.push_back(std::move(cmd));
1152
0
    }
1153
0
  }
1154
1155
  // Setup the proper working directory for the commands.
1156
0
  this->CreateCDCommand(commands1, dir, relative);
1157
1158
0
  cmGlobalUnixMakefileGenerator3* gg =
1159
0
    static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
1160
1161
  // Prefix the commands with the jobserver prefix "+"
1162
0
  if (ccg.GetCC().GetJobserverAware() && gg->IsGNUMakeJobServerAware()) {
1163
0
    std::transform(commands1.begin(), commands1.end(), commands1.begin(),
1164
0
                   [](std::string const& cmd) { return cmStrCat('+', cmd); });
1165
0
  } else if (ccg.GetCC().GetUsesTerminal() && gg->IsGNUMakeJobServerAware()) {
1166
    // Prefix USES_TERMINAL recipes with $(CMAKE_USES_TERMINAL_PREFIX): empty
1167
    // by default, but set to "+" by "cmake --build" under --output-sync so
1168
    // GNU Make leaves the recipe unbuffered and an interactive command keeps
1169
    // the terminal.  Jobserver-aware commands already carry "+" from the
1170
    // branch above and are excluded here to avoid a doubled prefix.
1171
0
    std::transform(commands1.begin(), commands1.end(), commands1.begin(),
1172
0
                   [](std::string const& cmd) {
1173
0
                     return cmStrCat("$(CMAKE_USES_TERMINAL_PREFIX)", cmd);
1174
0
                   });
1175
0
  }
1176
1177
  // push back the custom commands
1178
0
  cm::append(commands, commands1);
1179
0
}
1180
1181
void cmLocalUnixMakefileGenerator3::AppendCleanCommand(
1182
  std::vector<std::string>& commands, std::set<std::string> const& files,
1183
  cmGeneratorTarget* target, char const* filename)
1184
0
{
1185
0
  std::string cleanfile =
1186
0
    cmStrCat(target->GetSupportDirectory(), "/cmake_clean");
1187
0
  if (filename) {
1188
0
    cleanfile += "_";
1189
0
    cleanfile += filename;
1190
0
  }
1191
0
  cleanfile += ".cmake";
1192
0
  cmsys::ofstream fout(cleanfile.c_str());
1193
0
  if (!fout) {
1194
0
    cmSystemTools::Error("Could not create " + cleanfile);
1195
0
  }
1196
0
  if (!files.empty()) {
1197
0
    fout << "file(REMOVE_RECURSE\n";
1198
0
    for (std::string const& file : files) {
1199
0
      std::string fc = this->MaybeRelativeToCurBinDir(file);
1200
0
      fout << "  " << cmScriptGenerator::Quote(fc) << '\n';
1201
0
    }
1202
0
    fout << ")\n";
1203
0
  }
1204
0
  {
1205
0
    std::string remove = cmStrCat(
1206
0
      "$(CMAKE_COMMAND) -P ",
1207
0
      this->ConvertToOutputFormat(this->MaybeRelativeToCurBinDir(cleanfile),
1208
0
                                  cmOutputConverter::SHELL));
1209
0
    commands.push_back(std::move(remove));
1210
0
  }
1211
1212
  // For the main clean rule add per-language cleaning.
1213
0
  if (!filename) {
1214
    // Get the set of source languages in the target.
1215
0
    std::set<std::string> languages;
1216
0
    target->GetLanguages(
1217
0
      languages, this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
1218
0
    auto langFileDir = cmSystemTools::GetFilenamePath(
1219
0
      this->MaybeRelativeToCurBinDir(cleanfile));
1220
    /* clang-format off */
1221
0
    fout << "\n"
1222
0
            "# Per-language clean rules from dependency scanning.\n"
1223
0
            "foreach(lang " << cmJoin(languages, " ") << ")\n"
1224
0
            "  include(" << langFileDir
1225
0
         << "/cmake_clean_${lang}.cmake OPTIONAL)\n"
1226
0
            "endforeach()\n";
1227
    /* clang-format on */
1228
0
  }
1229
0
}
1230
1231
void cmLocalUnixMakefileGenerator3::AppendDirectoryCleanCommand(
1232
  std::vector<std::string>& commands)
1233
0
{
1234
0
  cmList cleanFiles;
1235
  // Look for additional files registered for cleaning in this directory.
1236
0
  if (cmValue prop_value =
1237
0
        this->Makefile->GetProperty("ADDITIONAL_CLEAN_FILES")) {
1238
0
    cleanFiles.assign(cmGeneratorExpression::Evaluate(
1239
0
      *prop_value, this,
1240
0
      this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")));
1241
0
  }
1242
0
  if (cleanFiles.empty()) {
1243
0
    return;
1244
0
  }
1245
1246
0
  auto const& rootLG = this->GetGlobalGenerator()->GetLocalGenerators().at(0);
1247
0
  std::string const& currentBinaryDir = this->GetCurrentBinaryDirectory();
1248
0
  std::string cleanfile =
1249
0
    cmStrCat(currentBinaryDir, "/CMakeFiles/cmake_directory_clean.cmake");
1250
  // Write clean script
1251
0
  {
1252
0
    cmsys::ofstream fout(cleanfile.c_str());
1253
0
    if (!fout) {
1254
0
      cmSystemTools::Error("Could not create " + cleanfile);
1255
0
      return;
1256
0
    }
1257
0
    fout << "file(REMOVE_RECURSE\n";
1258
0
    for (std::string const& cfl : cleanFiles) {
1259
0
      std::string fc = rootLG->MaybeRelativeToCurBinDir(
1260
0
        cmSystemTools::CollapseFullPath(cfl, currentBinaryDir));
1261
0
      fout << "  " << cmScriptGenerator::Quote(fc) << '\n';
1262
0
    }
1263
0
    fout << ")\n";
1264
0
  }
1265
  // Create command
1266
0
  {
1267
0
    std::string remove = cmStrCat(
1268
0
      "$(CMAKE_COMMAND) -P ",
1269
0
      this->ConvertToOutputFormat(rootLG->MaybeRelativeToCurBinDir(cleanfile),
1270
0
                                  cmOutputConverter::SHELL));
1271
0
    commands.push_back(std::move(remove));
1272
0
  }
1273
0
}
1274
1275
std::string cmLocalUnixMakefileGenerator3::TrimLongCommand(
1276
  std::string cmd, std::string& line) const
1277
0
{
1278
0
  cm::static_string_view longCommandFinalizer = "..."_s;
1279
1280
0
  size_t commandLineLimit = cmSystemTools::CalculateCommandLineLengthLimit();
1281
1282
0
  if (this->GetState()->UseBorlandMake()) {
1283
0
    constexpr size_t BORLAND_MAKE_COMMAND_LINE_LIMIT = 4096;
1284
0
    commandLineLimit =
1285
0
      std::min(commandLineLimit, BORLAND_MAKE_COMMAND_LINE_LIMIT);
1286
0
    if (commandLineLimit == 0) {
1287
0
      commandLineLimit = BORLAND_MAKE_COMMAND_LINE_LIMIT;
1288
0
    }
1289
0
  }
1290
1291
0
  if (commandLineLimit == 0) {
1292
    // No limit, just return the command as is.
1293
0
    return cmStrCat(std::move(cmd), this->EscapeForShell(line));
1294
0
  }
1295
1296
0
  size_t usedCommandLineLength = cmd.size() + longCommandFinalizer.size();
1297
1298
0
  if (usedCommandLineLength >= commandLineLimit) {
1299
0
    cmSystemTools::Error(cmStrCat(
1300
0
      "CMake command line length limit exceeded for command: ", cmd, line));
1301
0
    return cmd;
1302
0
  }
1303
1304
0
  size_t remainingSpace = commandLineLimit - usedCommandLineLength;
1305
0
  if (remainingSpace < line.size()) {
1306
0
    line.erase(remainingSpace);
1307
0
    line.append(longCommandFinalizer.data(), longCommandFinalizer.size());
1308
0
  }
1309
1310
  // re-escape the line: we cannot just trim the line, because it may contain
1311
  // characters that cannot be removed without their pairs:
1312
  // * quotes in the end of the line
1313
  // * backslash-escaped characters in the middle of the line
1314
0
  std::string escapedLine = this->EscapeForShell(line);
1315
0
  size_t overflowSize = escapedLine.size() > remainingSpace
1316
0
    ? escapedLine.size() - remainingSpace
1317
0
    : 0;
1318
0
  if (overflowSize > 0) {
1319
0
    line.erase(remainingSpace - overflowSize);
1320
0
    line.append(longCommandFinalizer.data(), longCommandFinalizer.size());
1321
0
    escapedLine = this->EscapeForShell(line);
1322
0
  }
1323
1324
0
  return cmStrCat(std::move(cmd), std::move(escapedLine));
1325
0
}
1326
1327
void cmLocalUnixMakefileGenerator3::AppendEcho(
1328
  std::vector<std::string>& commands, std::string const& text, EchoColor color,
1329
  EchoProgress const* progress)
1330
0
{
1331
  // Choose the color for the text.
1332
0
  std::string color_name;
1333
0
  if (this->GlobalGenerator->GetToolSupportsColor() && this->ColorMakefile) {
1334
    // See cmake::ExecuteEchoColor in cmake.cxx for these options.
1335
    // This color set is readable on both black and white backgrounds.
1336
0
    switch (color) {
1337
0
      case EchoNormal:
1338
0
        break;
1339
0
      case EchoDepend:
1340
0
        color_name = "--magenta --bold ";
1341
0
        break;
1342
0
      case EchoBuild:
1343
0
        color_name = "--green ";
1344
0
        break;
1345
0
      case EchoLink:
1346
0
        color_name = "--green --bold ";
1347
0
        break;
1348
0
      case EchoGenerate:
1349
0
        color_name = "--blue --bold ";
1350
0
        break;
1351
0
      case EchoGlobal:
1352
0
        color_name = "--cyan ";
1353
0
        break;
1354
0
    }
1355
0
  }
1356
1357
  // Echo one line at a time.
1358
0
  std::string line;
1359
0
  line.reserve(200);
1360
0
  for (char const* c = text.c_str();; ++c) {
1361
0
    if (*c == '\n' || *c == '\0') {
1362
      // Avoid writing a blank last line on end-of-string.
1363
0
      if (*c != '\0' || !line.empty()) {
1364
        // Add a command to echo this line.
1365
0
        std::string cmd;
1366
0
        if (color_name.empty() && !progress) {
1367
          // Use the native echo command.
1368
0
          cmd = cmStrCat("@echo ", this->EscapeForShell(line, false, true));
1369
0
        } else {
1370
          // Use cmake to echo the text in color.
1371
0
          cmd = cmStrCat(
1372
0
            "@$(CMAKE_COMMAND) -E cmake_echo_color \"--switch=$(COLOR)\" ",
1373
0
            color_name);
1374
0
          if (progress) {
1375
0
            cmd = cmStrCat(std::move(cmd), "--progress-dir=",
1376
0
                           this->ConvertToOutputFormat(
1377
0
                             progress->Dir, cmOutputConverter::SHELL),
1378
0
                           " --progress-num=", progress->Arg, ' ');
1379
0
          }
1380
0
          cmd = this->TrimLongCommand(std::move(cmd), line);
1381
0
        }
1382
0
        commands.emplace_back(std::move(cmd));
1383
0
      }
1384
1385
      // Reset the line to empty.
1386
0
      line.clear();
1387
1388
      // Progress appears only on first line.
1389
0
      progress = nullptr;
1390
1391
      // Terminate on end-of-string.
1392
0
      if (*c == '\0') {
1393
0
        return;
1394
0
      }
1395
0
    } else if (*c != '\r') {
1396
      // Append this character to the current line.
1397
0
      line += *c;
1398
0
    }
1399
0
  }
1400
0
}
1401
1402
std::string cmLocalUnixMakefileGenerator3::CreateMakeVariable(
1403
  std::string const& s, std::string const& s2)
1404
0
{
1405
0
  std::string unmodified = cmStrCat(s, s2);
1406
  // if there is no restriction on the length of make variables
1407
  // and there are no "." characters in the string, then return the
1408
  // unmodified combination.
1409
0
  if ((!this->MakefileVariableSize &&
1410
0
       unmodified.find('.') == std::string::npos) &&
1411
0
      (!this->MakefileVariableSize &&
1412
0
       unmodified.find('+') == std::string::npos) &&
1413
0
      (!this->MakefileVariableSize &&
1414
0
       unmodified.find('-') == std::string::npos)) {
1415
0
    return unmodified;
1416
0
  }
1417
1418
  // see if the variable has been defined before and return
1419
  // the modified version of the variable
1420
0
  auto i = this->MakeVariableMap.find(unmodified);
1421
0
  if (i != this->MakeVariableMap.end()) {
1422
0
    return i->second;
1423
0
  }
1424
  // start with the unmodified variable
1425
0
  std::string ret = unmodified;
1426
  // if this there is no value for this->MakefileVariableSize then
1427
  // the string must have bad characters in it
1428
0
  if (!this->MakefileVariableSize) {
1429
0
    std::replace(ret.begin(), ret.end(), '.', '_');
1430
0
    cmSystemTools::ReplaceString(ret, "-", "__");
1431
0
    cmSystemTools::ReplaceString(ret, "+", "___");
1432
0
    int ni = 0;
1433
0
    char buffer[12];
1434
    // make sure the _ version is not already used, if
1435
    // it is used then add number to the end of the variable
1436
0
    while (this->ShortMakeVariableMap.count(ret) && ni < 1000) {
1437
0
      ++ni;
1438
0
      snprintf(buffer, sizeof(buffer), "%04d", ni);
1439
0
      ret = unmodified + buffer;
1440
0
    }
1441
0
    this->ShortMakeVariableMap[ret] = "1";
1442
0
    this->MakeVariableMap[unmodified] = ret;
1443
0
    return ret;
1444
0
  }
1445
1446
  // if the string is greater than 32 chars it is an invalid variable name
1447
  // for borland make
1448
0
  if (static_cast<int>(ret.size()) > this->MakefileVariableSize) {
1449
0
    int keep = this->MakefileVariableSize - 8;
1450
0
    int size = keep + 3;
1451
0
    std::string str1 = s;
1452
0
    std::string str2 = s2;
1453
    // we must shorten the combined string by 4 characters
1454
    // keep no more than 24 characters from the second string
1455
0
    if (static_cast<int>(str2.size()) > keep) {
1456
0
      str2.resize(keep);
1457
0
    }
1458
0
    if (static_cast<int>(str1.size()) + static_cast<int>(str2.size()) > size) {
1459
0
      str1.resize(size - str2.size());
1460
0
    }
1461
0
    char buffer[12];
1462
0
    int ni = 0;
1463
0
    snprintf(buffer, sizeof(buffer), "%04d", ni);
1464
0
    ret = cmStrCat(str1, str2, buffer);
1465
0
    while (this->ShortMakeVariableMap.count(ret) && ni < 1000) {
1466
0
      ++ni;
1467
0
      snprintf(buffer, sizeof(buffer), "%04d", ni);
1468
0
      ret = cmStrCat(str1, str2, buffer);
1469
0
    }
1470
0
    if (ni == 1000) {
1471
0
      cmSystemTools::Error("Borland makefile variable length too long");
1472
0
      return unmodified;
1473
0
    }
1474
    // once an unused variable is found
1475
0
    this->ShortMakeVariableMap[ret] = "1";
1476
0
  }
1477
  // always make an entry into the unmodified to variable map
1478
0
  this->MakeVariableMap[unmodified] = ret;
1479
0
  return ret;
1480
0
}
1481
1482
bool cmLocalUnixMakefileGenerator3::UpdateDependencies(
1483
  std::string const& tgtInfo, std::string const& targetName, bool verbose,
1484
  bool color)
1485
0
{
1486
  // read in the target info file
1487
0
  if (!this->Makefile->ReadListFile(tgtInfo) ||
1488
0
      cmSystemTools::GetErrorOccurredFlag()) {
1489
0
    cmSystemTools::Error("Target DependInfo.cmake file not found");
1490
0
  }
1491
1492
0
  bool status = true;
1493
1494
  // Check if any multiple output pairs have a missing file.
1495
0
  this->CheckMultipleOutputs(verbose);
1496
1497
0
  auto echoColor = [color](std::string const& m) {
1498
0
    cm::StdIo::TermAttrSet attrs;
1499
0
    if (color) {
1500
0
      attrs = {
1501
0
        cm::StdIo::TermAttr::ForegroundMagenta,
1502
0
        cm::StdIo::TermAttr::ForegroundBold,
1503
0
      };
1504
0
    }
1505
0
    Print(cm::StdIo::Out(), attrs, m);
1506
0
    std::cout << std::endl;
1507
0
  };
1508
1509
0
  std::string const targetDir = cmSystemTools::GetFilenamePath(tgtInfo);
1510
0
  if (!this->Makefile->GetSafeDefinition("CMAKE_DEPENDS_LANGUAGES").empty()) {
1511
    // dependencies are managed by CMake itself
1512
1513
0
    std::string const internalDependFile = targetDir + "/depend.internal";
1514
0
    std::string const dependFile = targetDir + "/depend.make";
1515
1516
    // If the target DependInfo.cmake file has changed since the last
1517
    // time dependencies were scanned then force rescanning.  This may
1518
    // happen when a new source file is added and CMake regenerates the
1519
    // project but no other sources were touched.
1520
0
    bool needRescanDependInfo = false;
1521
0
    cmFileTimeCache* ftc =
1522
0
      this->GlobalGenerator->GetCMakeInstance()->GetFileTimeCache();
1523
0
    {
1524
0
      int result;
1525
0
      if (!ftc->Compare(internalDependFile, tgtInfo, &result) || result < 0) {
1526
0
        if (verbose) {
1527
0
          cmSystemTools::Stdout(cmStrCat("Dependee \"", tgtInfo,
1528
0
                                         "\" is newer than depender \"",
1529
0
                                         internalDependFile, "\".\n"));
1530
0
        }
1531
0
        needRescanDependInfo = true;
1532
0
      }
1533
0
    }
1534
1535
    // If the directory information is newer than depend.internal, include
1536
    // dirs may have changed. In this case discard all old dependencies.
1537
0
    bool needRescanDirInfo = false;
1538
0
    {
1539
0
      std::string dirInfoFile =
1540
0
        cmStrCat(this->GetCurrentBinaryDirectory(),
1541
0
                 "/CMakeFiles/CMakeDirectoryInformation.cmake");
1542
0
      int result;
1543
0
      if (!ftc->Compare(internalDependFile, dirInfoFile, &result) ||
1544
0
          result < 0) {
1545
0
        if (verbose) {
1546
0
          cmSystemTools::Stdout(cmStrCat("Dependee \"", dirInfoFile,
1547
0
                                         "\" is newer than depender \"",
1548
0
                                         internalDependFile, "\".\n"));
1549
0
        }
1550
0
        needRescanDirInfo = true;
1551
0
      }
1552
0
    }
1553
1554
    // Check the implicit dependencies to see if they are up to date.
1555
    // The build.make file may have explicit dependencies for the object
1556
    // files but these will not affect the scanning process so they need
1557
    // not be considered.
1558
0
    cmDepends::DependencyMap validDependencies;
1559
0
    bool needRescanDependencies = false;
1560
0
    if (!needRescanDirInfo) {
1561
0
      cmDependsC checker;
1562
0
      checker.SetVerbose(verbose);
1563
0
      checker.SetFileTimeCache(ftc);
1564
      // cmDependsC::Check() fills the vector validDependencies() with the
1565
      // dependencies for those files where they are still valid, i.e.
1566
      // neither the files themselves nor any files they depend on have
1567
      // changed. We don't do that if the CMakeDirectoryInformation.cmake
1568
      // file has changed, because then potentially all dependencies have
1569
      // changed. This information is given later on to cmDependsC, which
1570
      // then only rescans the files where it did not get valid dependencies
1571
      // via this dependency vector. This means that in the normal case, when
1572
      // only few or one file have been edited, then also only this one file
1573
      // is actually scanned again, instead of all files for this target.
1574
0
      needRescanDependencies =
1575
0
        !checker.Check(dependFile, internalDependFile, validDependencies);
1576
0
    }
1577
1578
0
    if (needRescanDependInfo || needRescanDirInfo || needRescanDependencies) {
1579
      // The dependencies must be regenerated.
1580
0
      if (verbose) {
1581
0
        std::string message =
1582
0
          cmStrCat("Scanning dependencies of target ", targetName);
1583
0
        echoColor(message);
1584
0
      }
1585
1586
0
      status = this->ScanDependencies(targetDir, dependFile,
1587
0
                                      internalDependFile, validDependencies);
1588
0
    }
1589
0
  }
1590
1591
0
  auto depends =
1592
0
    this->Makefile->GetSafeDefinition("CMAKE_DEPENDS_DEPENDENCY_FILES");
1593
0
  if (!depends.empty()) {
1594
    // dependencies are managed by compiler
1595
0
    cmList depFiles{ depends, cmList::EmptyElements::Yes };
1596
0
    std::string const internalDepFile =
1597
0
      targetDir + "/compiler_depend.internal";
1598
0
    std::string const depFile = targetDir + "/compiler_depend.make";
1599
0
    cmDepends::DependencyMap dependencies;
1600
0
    cmDependsCompiler depsManager;
1601
0
    bool projectOnly = cmIsOn(
1602
0
      this->Makefile->GetSafeDefinition("CMAKE_DEPENDS_IN_PROJECT_ONLY"));
1603
1604
0
    depsManager.SetVerbose(verbose);
1605
0
    depsManager.SetLocalGenerator(this);
1606
1607
0
    if (!depsManager.CheckDependencies(
1608
0
          internalDepFile, depFiles, dependencies,
1609
0
          projectOnly ? NotInProjectDir(this->GetSourceDirectory(),
1610
0
                                        this->GetBinaryDirectory())
1611
0
                      : std::function<bool(std::string const&)>())) {
1612
      // regenerate dependencies files
1613
0
      if (verbose) {
1614
0
        auto message =
1615
0
          cmStrCat("Consolidate compiler generated dependencies of target ",
1616
0
                   targetName);
1617
0
        echoColor(message);
1618
0
      }
1619
1620
      // Open the make depends file.  This should be copy-if-different
1621
      // because the make tool may try to reload it needlessly otherwise.
1622
0
      cmGeneratedFileStream ruleFileStream(
1623
0
        depFile, false, this->GlobalGenerator->GetMakefileEncoding());
1624
0
      ruleFileStream.SetCopyIfDifferent(true);
1625
0
      if (!ruleFileStream) {
1626
0
        return false;
1627
0
      }
1628
1629
      // Open the cmake dependency tracking file.  This should not be
1630
      // copy-if-different because dependencies are re-scanned when it is
1631
      // older than the DependInfo.cmake.
1632
0
      cmGeneratedFileStream internalRuleFileStream(
1633
0
        internalDepFile, false, this->GlobalGenerator->GetMakefileEncoding());
1634
0
      if (!internalRuleFileStream) {
1635
0
        return false;
1636
0
      }
1637
1638
0
      this->WriteDisclaimer(ruleFileStream);
1639
0
      this->WriteDisclaimer(internalRuleFileStream);
1640
1641
0
      depsManager.WriteDependencies(dependencies, ruleFileStream,
1642
0
                                    internalRuleFileStream);
1643
0
    }
1644
0
  }
1645
1646
  // The dependencies are already up-to-date.
1647
0
  return status;
1648
0
}
1649
1650
bool cmLocalUnixMakefileGenerator3::ScanDependencies(
1651
  std::string const& targetDir, std::string const& dependFile,
1652
  std::string const& internalDependFile, cmDepends::DependencyMap& validDeps)
1653
0
{
1654
  // Read the directory information file.
1655
0
  cmMakefile* mf = this->Makefile;
1656
0
  bool haveDirectoryInfo = false;
1657
0
  {
1658
0
    std::string dirInfoFile =
1659
0
      cmStrCat(this->GetCurrentBinaryDirectory(),
1660
0
               "/CMakeFiles/CMakeDirectoryInformation.cmake");
1661
0
    if (mf->ReadListFile(dirInfoFile) &&
1662
0
        !cmSystemTools::GetErrorOccurredFlag()) {
1663
0
      haveDirectoryInfo = true;
1664
0
    }
1665
0
  }
1666
1667
  // Lookup useful directory information.
1668
0
  if (haveDirectoryInfo) {
1669
    // Test whether we need to force Unix paths.
1670
0
    if (cmValue force = mf->GetDefinition("CMAKE_FORCE_UNIX_PATHS")) {
1671
0
      if (!force.IsOff()) {
1672
0
        cmSystemTools::SetForceUnixPaths(true);
1673
0
      }
1674
0
    }
1675
1676
    // Setup relative path top directories.
1677
0
    cmValue relativePathTopSource =
1678
0
      mf->GetDefinition("CMAKE_RELATIVE_PATH_TOP_SOURCE");
1679
0
    cmValue relativePathTopBinary =
1680
0
      mf->GetDefinition("CMAKE_RELATIVE_PATH_TOP_BINARY");
1681
0
    if (relativePathTopSource && relativePathTopBinary) {
1682
0
      this->SetRelativePathTop(*relativePathTopSource, *relativePathTopBinary);
1683
0
    }
1684
0
  } else {
1685
0
    cmSystemTools::Error("Directory Information file not found");
1686
0
  }
1687
1688
  // Open the make depends file.  This should be copy-if-different
1689
  // because the make tool may try to reload it needlessly otherwise.
1690
0
  cmGeneratedFileStream ruleFileStream(
1691
0
    dependFile, false, this->GlobalGenerator->GetMakefileEncoding());
1692
0
  ruleFileStream.SetCopyIfDifferent(true);
1693
0
  if (!ruleFileStream) {
1694
0
    return false;
1695
0
  }
1696
1697
  // Open the cmake dependency tracking file.  This should not be
1698
  // copy-if-different because dependencies are re-scanned when it is
1699
  // older than the DependInfo.cmake.
1700
0
  cmGeneratedFileStream internalRuleFileStream(
1701
0
    internalDependFile, false, this->GlobalGenerator->GetMakefileEncoding());
1702
0
  if (!internalRuleFileStream) {
1703
0
    return false;
1704
0
  }
1705
1706
0
  this->WriteDisclaimer(ruleFileStream);
1707
0
  this->WriteDisclaimer(internalRuleFileStream);
1708
1709
  // for each language we need to scan, scan it
1710
0
  cmList langs{ mf->GetSafeDefinition("CMAKE_DEPENDS_LANGUAGES") };
1711
0
  for (std::string const& lang : langs) {
1712
    // construct the checker
1713
    // Create the scanner for this language
1714
0
    std::unique_ptr<cmDepends> scanner;
1715
0
    if (lang == "C" || lang == "CXX" || lang == "RC" || lang == "ASM" ||
1716
0
        lang == "OBJC" || lang == "OBJCXX" || lang == "CUDA" ||
1717
0
        lang == "HIP" || lang == "ISPC" || lang == "SYCL") {
1718
      // TODO: Handle RC (resource files) dependencies correctly.
1719
0
      scanner = cm::make_unique<cmDependsC>(this, targetDir, lang, &validDeps);
1720
0
    }
1721
0
#ifndef CMAKE_BOOTSTRAP
1722
0
    else if (lang == "Fortran") {
1723
0
      ruleFileStream << "# Note that incremental build could trigger "
1724
0
                        "a call to cmake_copy_f90_mod on each re-build\n";
1725
0
      scanner = cm::make_unique<cmDependsFortran>(this);
1726
0
    } else if (lang == "Java") {
1727
0
      scanner = cm::make_unique<cmDependsJava>();
1728
0
    }
1729
0
#endif
1730
1731
0
    if (scanner) {
1732
0
      scanner->SetLocalGenerator(this);
1733
0
      scanner->SetFileTimeCache(
1734
0
        this->GlobalGenerator->GetCMakeInstance()->GetFileTimeCache());
1735
0
      scanner->SetLanguage(lang);
1736
0
      scanner->SetTargetDirectory(targetDir);
1737
0
      scanner->Write(ruleFileStream, internalRuleFileStream);
1738
0
    }
1739
0
  }
1740
1741
0
  return true;
1742
0
}
1743
1744
void cmLocalUnixMakefileGenerator3::CheckMultipleOutputs(bool verbose)
1745
0
{
1746
0
  cmMakefile* mf = this->Makefile;
1747
1748
  // Get the string listing the multiple output pairs.
1749
0
  cmValue pairs_string = mf->GetDefinition("CMAKE_MULTIPLE_OUTPUT_PAIRS");
1750
0
  if (!pairs_string) {
1751
0
    return;
1752
0
  }
1753
1754
  // Convert the string to a list and preserve empty entries.
1755
0
  cmList pairs{ *pairs_string, cmList::EmptyElements::Yes };
1756
0
  for (auto i = pairs.begin(); i != pairs.end() && (i + 1) != pairs.end();) {
1757
0
    std::string const& depender = *i++;
1758
0
    std::string const& dependee = *i++;
1759
1760
    // If the depender is missing then delete the dependee to make
1761
    // sure both will be regenerated.
1762
0
    if (cmSystemTools::FileExists(dependee) &&
1763
0
        !cmSystemTools::FileExists(depender)) {
1764
0
      if (verbose) {
1765
0
        cmSystemTools::Stdout(cmStrCat(
1766
0
          "Deleting primary custom command output \"", dependee,
1767
0
          "\" because another output \"", depender, "\" does not exist.\n"));
1768
0
      }
1769
0
      cmSystemTools::RemoveFile(dependee);
1770
0
    }
1771
0
  }
1772
0
}
1773
1774
void cmLocalUnixMakefileGenerator3::WriteLocalAllRules(
1775
  std::ostream& ruleFileStream)
1776
0
{
1777
0
  this->WriteDisclaimer(ruleFileStream);
1778
1779
  // Write the main entry point target.  This must be the VERY first
1780
  // target so that make with no arguments will run it.
1781
0
  {
1782
    // Just depend on the all target to drive the build.
1783
0
    std::vector<std::string> depends;
1784
0
    std::vector<std::string> no_commands;
1785
0
    depends.emplace_back("all");
1786
1787
    // Write the rule.
1788
0
    this->WriteMakeRule(ruleFileStream,
1789
0
                        "Default target executed when no arguments are "
1790
0
                        "given to make.",
1791
0
                        "default_target", depends, no_commands, true);
1792
1793
    // Help out users that try "gmake target1 target2 -j".
1794
0
    cmGlobalUnixMakefileGenerator3* gg =
1795
0
      static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
1796
0
    if (gg->AllowNotParallel()) {
1797
0
      std::vector<std::string> no_depends;
1798
0
      this->WriteMakeRule(ruleFileStream,
1799
0
                          "Allow only one \"make -f "
1800
0
                          "Makefile2\" at a time, but pass "
1801
0
                          "parallelism.",
1802
0
                          ".NOTPARALLEL", no_depends, no_commands, false);
1803
0
    }
1804
0
  }
1805
1806
0
  this->WriteSpecialTargetsTop(ruleFileStream);
1807
1808
  // Include the progress variables for the target.
1809
  // Write all global targets
1810
0
  this->WriteDivider(ruleFileStream);
1811
0
  ruleFileStream << "# Targets provided globally by CMake.\n"
1812
0
                    "\n";
1813
0
  auto const& targets = this->GetGeneratorTargets();
1814
0
  for (auto const& gt : targets) {
1815
0
    if (gt->GetType() == cm::TargetType::GLOBAL_TARGET) {
1816
0
      std::string targetString =
1817
0
        "Special rule for the target " + gt->GetName();
1818
0
      std::vector<std::string> commands;
1819
0
      std::vector<std::string> depends;
1820
1821
0
      cmValue p = gt->GetProperty("EchoString");
1822
0
      char const* text = p ? p->c_str() : "Running external command ...";
1823
0
      depends.reserve(gt->GetUtilities().size());
1824
0
      for (BT<std::pair<std::string, bool>> const& u : gt->GetUtilities()) {
1825
0
        depends.push_back(u.Value.first);
1826
0
      }
1827
0
      this->AppendEcho(commands, text,
1828
0
                       cmLocalUnixMakefileGenerator3::EchoGlobal);
1829
1830
      // Global targets store their rules in pre- and post-build commands.
1831
0
      this->AppendCustomDepends(depends, gt->GetPreBuildCommands());
1832
0
      this->AppendCustomDepends(depends, gt->GetPostBuildCommands());
1833
0
      this->AppendCustomCommands(commands, gt->GetPreBuildCommands(), gt.get(),
1834
0
                                 this->GetCurrentBinaryDirectory());
1835
0
      this->AppendCustomCommands(commands, gt->GetPostBuildCommands(),
1836
0
                                 gt.get(), this->GetCurrentBinaryDirectory());
1837
0
      std::string targetName = gt->GetName();
1838
0
      this->WriteMakeRule(ruleFileStream, targetString.c_str(), targetName,
1839
0
                          depends, commands, true);
1840
1841
      // Provide a "/fast" version of the target.
1842
0
      depends.clear();
1843
0
      if ((targetName == "install") || (targetName == "install/local") ||
1844
0
          (targetName == "install/strip")) {
1845
        // Provide a fast install target that does not depend on all
1846
        // but has the same command.
1847
0
        depends.emplace_back("preinstall/fast");
1848
0
      } else {
1849
        // Just forward to the real target so at least it will work.
1850
0
        depends.push_back(targetName);
1851
0
        commands.clear();
1852
0
      }
1853
0
      targetName += "/fast";
1854
0
      this->WriteMakeRule(ruleFileStream, targetString.c_str(), targetName,
1855
0
                          depends, commands, true);
1856
0
    }
1857
0
  }
1858
1859
0
  std::vector<std::string> depends;
1860
0
  std::vector<std::string> commands;
1861
1862
  // Write the all rule.
1863
0
  std::string recursiveTarget =
1864
0
    cmStrCat(this->GetCurrentBinaryDirectory(), "/all");
1865
1866
0
  bool regenerate =
1867
0
    !this->GlobalGenerator->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION");
1868
0
  if (regenerate) {
1869
0
    depends.emplace_back("cmake_check_build_system");
1870
0
  }
1871
1872
0
  std::string const progressDir = this->ConvertToOutputFormat(
1873
0
    cmStrCat(this->GetBinaryDirectory(), "/CMakeFiles"),
1874
0
    cmOutputConverter::SHELL);
1875
0
  std::string const progressMarks = this->ConvertToOutputFormat(
1876
0
    this->ConvertToFullPath("/CMakeFiles/progress.marks"),
1877
0
    cmOutputConverter::SHELL);
1878
0
  std::string const progressStartCommand =
1879
0
    cmStrCat("$(CMAKE_COMMAND) -E cmake_progress_start ", progressDir, ' ',
1880
0
             progressMarks);
1881
0
  std::string const progressFinishCommand =
1882
0
    cmStrCat("$(CMAKE_COMMAND) -E cmake_progress_start ", progressDir, " 0");
1883
1884
0
  commands.emplace_back(progressStartCommand);
1885
0
  std::string mf2Dir = "CMakeFiles/Makefile2";
1886
0
  commands.push_back(this->GetRecursiveMakeCall(mf2Dir, recursiveTarget));
1887
0
  this->CreateCDCommand(commands, this->GetBinaryDirectory(),
1888
0
                        this->GetCurrentBinaryDirectory());
1889
0
  commands.emplace_back(progressFinishCommand);
1890
0
  this->WriteMakeRule(ruleFileStream, "The main all target", "all", depends,
1891
0
                      commands, true);
1892
1893
  // Write the codegen rule.
1894
0
  if (this->GetGlobalGenerator()->CheckCMP0171()) {
1895
0
    recursiveTarget = cmStrCat(this->GetCurrentBinaryDirectory(), "/codegen");
1896
0
    depends.clear();
1897
0
    commands.clear();
1898
0
    if (regenerate) {
1899
0
      depends.emplace_back("cmake_check_build_system");
1900
0
    }
1901
0
    commands.emplace_back(progressStartCommand);
1902
0
    commands.push_back(this->GetRecursiveMakeCall(mf2Dir, recursiveTarget));
1903
0
    this->CreateCDCommand(commands, this->GetBinaryDirectory(),
1904
0
                          this->GetCurrentBinaryDirectory());
1905
0
    commands.emplace_back(progressFinishCommand);
1906
0
    this->WriteMakeRule(ruleFileStream, "The main codegen target", "codegen",
1907
0
                        depends, commands, true);
1908
0
  }
1909
1910
  // Write the clean rule.
1911
0
  recursiveTarget = cmStrCat(this->GetCurrentBinaryDirectory(), "/clean");
1912
0
  commands.clear();
1913
0
  depends.clear();
1914
0
  commands.push_back(this->GetRecursiveMakeCall(mf2Dir, recursiveTarget));
1915
0
  this->CreateCDCommand(commands, this->GetBinaryDirectory(),
1916
0
                        this->GetCurrentBinaryDirectory());
1917
0
  this->WriteMakeRule(ruleFileStream, "The main clean target", "clean",
1918
0
                      depends, commands, true);
1919
0
  commands.clear();
1920
0
  depends.clear();
1921
0
  depends.emplace_back("clean");
1922
0
  this->WriteMakeRule(ruleFileStream, "The main clean target", "clean/fast",
1923
0
                      depends, commands, true);
1924
1925
  // Write the preinstall rule.
1926
0
  recursiveTarget = cmStrCat(this->GetCurrentBinaryDirectory(), "/preinstall");
1927
0
  commands.clear();
1928
0
  depends.clear();
1929
0
  cmValue noall =
1930
0
    this->Makefile->GetDefinition("CMAKE_SKIP_INSTALL_ALL_DEPENDENCY");
1931
0
  if (noall.IsOff()) {
1932
    // Drive the build before installing.
1933
0
    depends.emplace_back("all");
1934
0
  } else if (regenerate) {
1935
    // At least make sure the build system is up to date.
1936
0
    depends.emplace_back("cmake_check_build_system");
1937
0
  }
1938
0
  commands.push_back(this->GetRecursiveMakeCall(mf2Dir, recursiveTarget));
1939
0
  this->CreateCDCommand(commands, this->GetBinaryDirectory(),
1940
0
                        this->GetCurrentBinaryDirectory());
1941
0
  this->WriteMakeRule(ruleFileStream, "Prepare targets for installation.",
1942
0
                      "preinstall", depends, commands, true);
1943
0
  depends.clear();
1944
0
  this->WriteMakeRule(ruleFileStream, "Prepare targets for installation.",
1945
0
                      "preinstall/fast", depends, commands, true);
1946
1947
0
  if (regenerate) {
1948
    // write the depend rule, really a recompute depends rule
1949
0
    depends.clear();
1950
0
    commands.clear();
1951
0
    cmake* cm = this->GlobalGenerator->GetCMakeInstance();
1952
0
    if (cm->DoWriteGlobVerifyTarget()) {
1953
0
      std::string rescanRule =
1954
0
        cmStrCat("$(CMAKE_COMMAND) -P ",
1955
0
                 this->ConvertToOutputFormat(cm->GetGlobVerifyScript(),
1956
0
                                             cmOutputConverter::SHELL));
1957
0
      commands.push_back(rescanRule);
1958
0
    }
1959
0
    std::string cmakefileName = "CMakeFiles/Makefile.cmake";
1960
0
    {
1961
0
      std::string runRule = cmStrCat(
1962
0
        "$(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) ",
1963
0
        cm->GetIgnoreCompileWarningAsError() ? "--compile-no-warning-as-error "
1964
0
                                             : "",
1965
0
        cm->GetIgnoreLinkWarningAsError() ? "--link-no-warning-as-error " : "",
1966
0
        "--check-build-system ",
1967
0
        this->ConvertToOutputFormat(cmakefileName, cmOutputConverter::SHELL),
1968
0
        " 1");
1969
0
      commands.push_back(std::move(runRule));
1970
0
#ifndef CMAKE_BOOTSTRAP
1971
      // FIXME(#27079): This does not work for MSYS Makefiles.
1972
0
      if (this->GlobalGenerator->GetName() != "MSYS Makefiles") {
1973
0
        addInstrumentationCommand(
1974
0
          this->GetCMakeInstance()->GetInstrumentation(), commands);
1975
0
      }
1976
0
#endif
1977
0
    }
1978
0
    this->CreateCDCommand(commands, this->GetBinaryDirectory(),
1979
0
                          this->GetCurrentBinaryDirectory());
1980
0
    this->WriteMakeRule(ruleFileStream, "clear depends", "depend", depends,
1981
0
                        commands, true);
1982
0
  }
1983
0
}
1984
1985
void cmLocalUnixMakefileGenerator3::ClearDependencies(cmMakefile* mf,
1986
                                                      bool verbose)
1987
0
{
1988
  // Get the list of target files to check
1989
0
  cmValue infoDef = mf->GetDefinition("CMAKE_DEPEND_INFO_FILES");
1990
0
  if (!infoDef) {
1991
0
    return;
1992
0
  }
1993
0
  cmList files{ *infoDef };
1994
1995
  // Each depend information file corresponds to a target.  Clear the
1996
  // dependencies for that target.
1997
0
  cmDepends clearer;
1998
0
  clearer.SetVerbose(verbose);
1999
0
  for (std::string const& file : files) {
2000
0
    auto snapshot = mf->GetState()->CreateBaseSnapshot();
2001
0
    cmMakefile lmf(mf->GetGlobalGenerator(), snapshot);
2002
0
    lmf.ReadListFile(file);
2003
2004
0
    if (!lmf.GetSafeDefinition("CMAKE_DEPENDS_LANGUAGES").empty()) {
2005
0
      std::string dir = cmSystemTools::GetFilenamePath(file);
2006
2007
      // Clear the implicit dependency makefile.
2008
0
      std::string dependFile = dir + "/depend.make";
2009
0
      clearer.Clear(dependFile);
2010
2011
      // Remove the internal dependency check file to force
2012
      // regeneration.
2013
0
      std::string internalDependFile = dir + "/depend.internal";
2014
0
      cmSystemTools::RemoveFile(internalDependFile);
2015
0
    }
2016
2017
0
    auto depsFiles = lmf.GetSafeDefinition("CMAKE_DEPENDS_DEPENDENCY_FILES");
2018
0
    if (!depsFiles.empty()) {
2019
0
      auto dir = cmCMakePath(file).GetParentPath();
2020
      // Clear the implicit dependency makefile.
2021
0
      auto depFile = cmCMakePath(dir).Append("compiler_depend.make");
2022
0
      clearer.Clear(depFile.GenericString());
2023
2024
      // Remove the internal dependency check file
2025
0
      auto internalDepFile =
2026
0
        cmCMakePath(dir).Append("compiler_depend.internal");
2027
0
      cmSystemTools::RemoveFile(internalDepFile.GenericString());
2028
2029
      // Touch timestamp file to force dependencies regeneration
2030
0
      auto DepTimestamp = cmCMakePath(dir).Append("compiler_depend.ts");
2031
0
      cmSystemTools::Touch(DepTimestamp.GenericString(), true);
2032
2033
      // clear the dependencies files generated by the compiler
2034
0
      cmList dependencies{ depsFiles, cmList::EmptyElements::Yes };
2035
0
      cmDependsCompiler depsManager;
2036
0
      depsManager.SetVerbose(verbose);
2037
0
      depsManager.ClearDependencies(dependencies);
2038
0
    }
2039
0
  }
2040
0
}
2041
2042
void cmLocalUnixMakefileGenerator3::WriteDependLanguageInfo(
2043
  std::ostream& cmakefileStream, cmGeneratorTarget* target)
2044
0
{
2045
  // To enable dependencies filtering
2046
0
  cmakefileStream << "\n"
2047
0
                     "# Consider dependencies only in project.\n"
2048
0
                     "set(CMAKE_DEPENDS_IN_PROJECT_ONLY "
2049
0
                  << (cmIsOn(this->Makefile->GetSafeDefinition(
2050
0
                        "CMAKE_DEPENDS_IN_PROJECT_ONLY"))
2051
0
                        ? "ON"
2052
0
                        : "OFF")
2053
0
                  << ")\n\n";
2054
2055
0
  bool requireFortran = false;
2056
0
  if (target->HaveFortranSources(this->GetConfigName())) {
2057
0
    requireFortran = true;
2058
0
  }
2059
2060
0
  auto const& implicitLangs =
2061
0
    this->GetImplicitDepends(target, cmDependencyScannerKind::CMake);
2062
2063
  // list the languages
2064
0
  cmakefileStream << "# The set of languages for which implicit "
2065
0
                     "dependencies are needed:\n"
2066
0
                     "set(CMAKE_DEPENDS_LANGUAGES\n";
2067
0
  for (auto const& implicitLang : implicitLangs) {
2068
0
    cmakefileStream << "  \"" << implicitLang.first << "\"\n";
2069
0
    if (requireFortran && implicitLang.first == "Fortran"_s) {
2070
0
      requireFortran = false;
2071
0
    }
2072
0
  }
2073
0
  if (requireFortran) {
2074
0
    cmakefileStream << "  \"Fortran\"\n";
2075
0
  }
2076
0
  cmakefileStream << "  )\n";
2077
2078
0
  if (!implicitLangs.empty()) {
2079
    // now list the files for each language
2080
0
    cmakefileStream
2081
0
      << "# The set of files for implicit dependencies of each language:\n";
2082
0
    for (auto const& implicitLang : implicitLangs) {
2083
0
      auto const& lang = implicitLang.first;
2084
2085
0
      cmakefileStream << "set(CMAKE_DEPENDS_CHECK_" << lang << '\n';
2086
0
      auto const& implicitPairs = implicitLang.second;
2087
2088
      // for each file pair
2089
0
      for (auto const& implicitPair : implicitPairs) {
2090
0
        for (auto const& di : implicitPair.second) {
2091
0
          cmakefileStream << "  \"" << di << "\" \"" << implicitPair.first
2092
0
                          << "\"\n";
2093
0
        }
2094
0
      }
2095
0
      cmakefileStream << "  )\n";
2096
2097
      // Tell the dependency scanner what compiler is used.
2098
0
      std::string cidVar = cmStrCat("CMAKE_", lang, "_COMPILER_ID");
2099
0
      cmValue cid = this->Makefile->GetDefinition(cidVar);
2100
0
      if (cmNonempty(cid)) {
2101
0
        cmakefileStream << "set(CMAKE_" << lang << "_COMPILER_ID \"" << *cid
2102
0
                        << "\")\n";
2103
0
      }
2104
2105
0
      if (lang == "Fortran") {
2106
0
        std::string smodSep =
2107
0
          this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_SEP");
2108
0
        std::string smodExt =
2109
0
          this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_EXT");
2110
0
        cmakefileStream << "set(CMAKE_Fortran_SUBMODULE_SEP \"" << smodSep
2111
0
                        << "\")\n"
2112
0
                           "set(CMAKE_Fortran_SUBMODULE_EXT \""
2113
0
                        << smodExt << "\")\n";
2114
0
      }
2115
2116
      // Build a list of preprocessor definitions for the target.
2117
0
      std::set<std::string> defines;
2118
0
      this->GetTargetDefines(target, this->GetConfigName(), lang, defines);
2119
0
      if (!defines.empty()) {
2120
0
        cmakefileStream << "\n"
2121
0
                           "# Preprocessor definitions for this target.\n"
2122
0
                           "set(CMAKE_TARGET_DEFINITIONS_"
2123
0
                        << lang << '\n';
2124
0
        for (std::string const& define : defines) {
2125
0
          cmakefileStream << "  " << cmScriptGenerator::Quote(define) << '\n';
2126
0
        }
2127
0
        cmakefileStream << "  )\n";
2128
0
      }
2129
2130
      // Target-specific include directories:
2131
0
      cmakefileStream << "\n"
2132
0
                         "# The include file search paths:\n"
2133
0
                         "set(CMAKE_"
2134
0
                      << lang << "_TARGET_INCLUDE_PATH\n";
2135
0
      std::vector<std::string> includes;
2136
2137
0
      this->GetIncludeDirectories(includes, target, lang,
2138
0
                                  this->GetConfigName());
2139
0
      std::string const& binaryDir = this->GetState()->GetBinaryDirectory();
2140
0
      if (this->Makefile->IsOn("CMAKE_DEPENDS_IN_PROJECT_ONLY")) {
2141
0
        std::string const& sourceDir = this->GetState()->GetSourceDirectory();
2142
0
        cm::erase_if(includes, ::NotInProjectDir(sourceDir, binaryDir));
2143
0
      }
2144
0
      for (std::string const& include : includes) {
2145
0
        cmakefileStream << "  \"" << this->MaybeRelativeToTopBinDir(include)
2146
0
                        << "\"\n";
2147
0
      }
2148
0
      cmakefileStream << "  )\n";
2149
0
    }
2150
2151
    // Store include transform rule properties.  Write the directory
2152
    // rules first because they may be overridden by later target rules.
2153
0
    cmList transformRules;
2154
0
    if (cmValue xform =
2155
0
          this->Makefile->GetProperty("IMPLICIT_DEPENDS_INCLUDE_TRANSFORM")) {
2156
0
      transformRules.assign(*xform);
2157
0
    }
2158
0
    if (cmValue xform =
2159
0
          target->GetProperty("IMPLICIT_DEPENDS_INCLUDE_TRANSFORM")) {
2160
0
      transformRules.append(*xform);
2161
0
    }
2162
0
    if (!transformRules.empty()) {
2163
0
      cmakefileStream << "\nset(CMAKE_INCLUDE_TRANSFORMS\n";
2164
0
      for (std::string const& tr : transformRules) {
2165
0
        cmakefileStream << "  " << cmScriptGenerator::Quote(tr) << '\n';
2166
0
      }
2167
0
      cmakefileStream << "  )\n";
2168
0
    }
2169
0
  }
2170
2171
0
  auto const& compilerLangs =
2172
0
    this->GetImplicitDepends(target, cmDependencyScannerKind::Compiler);
2173
2174
  // list the dependency files managed by the compiler
2175
0
  cmakefileStream << "\n# The set of dependency files which are needed:\n"
2176
0
                     "set(CMAKE_DEPENDS_DEPENDENCY_FILES\n";
2177
0
  for (auto const& compilerLang : compilerLangs) {
2178
0
    auto const& compilerPairs = compilerLang.second;
2179
0
    if (compilerLang.first == "CUSTOM"_s) {
2180
0
      for (auto const& compilerPair : compilerPairs) {
2181
0
        for (auto const& src : compilerPair.second) {
2182
0
          cmakefileStream << R"(  "" ")"
2183
0
                          << this->MaybeRelativeToTopBinDir(compilerPair.first)
2184
0
                          << R"(" "custom" ")"
2185
0
                          << this->MaybeRelativeToTopBinDir(src) << "\"\n";
2186
0
        }
2187
0
      }
2188
0
    } else if (compilerLang.first == "LINK"_s) {
2189
0
      auto depFormat = this->Makefile->GetDefinition(
2190
0
        cmStrCat("CMAKE_", target->GetLinkerLanguage(this->GetConfigName()),
2191
0
                 "_LINKER_DEPFILE_FORMAT"));
2192
0
      for (auto const& compilerPair : compilerPairs) {
2193
0
        for (auto const& src : compilerPair.second) {
2194
0
          cmakefileStream << R"(  "" ")"
2195
0
                          << this->MaybeRelativeToTopBinDir(compilerPair.first)
2196
0
                          << "\" \"" << depFormat << "\" \""
2197
0
                          << this->MaybeRelativeToTopBinDir(src) << "\"\n";
2198
0
        }
2199
0
      }
2200
0
    } else {
2201
0
      auto depFormat = this->Makefile->GetSafeDefinition(
2202
0
        cmStrCat("CMAKE_", compilerLang.first, "_DEPFILE_FORMAT"));
2203
0
      for (auto const& compilerPair : compilerPairs) {
2204
0
        for (auto const& src : compilerPair.second) {
2205
0
          cmakefileStream << "  \"" << src << "\" \""
2206
0
                          << this->MaybeRelativeToTopBinDir(compilerPair.first)
2207
0
                          << "\" \"" << depFormat << "\" \""
2208
0
                          << this->MaybeRelativeToTopBinDir(compilerPair.first)
2209
0
                          << ".d\"\n";
2210
0
        }
2211
0
      }
2212
0
    }
2213
0
  }
2214
0
  cmakefileStream << "  )\n";
2215
0
}
2216
2217
void cmLocalUnixMakefileGenerator3::WriteDisclaimer(std::ostream& os)
2218
0
{
2219
0
  os << "# CMAKE generated file: DO NOT EDIT!\n"
2220
0
        "# Generated by \""
2221
0
     << this->GlobalGenerator->GetName()
2222
0
     << "\""
2223
0
        " Generator, CMake Version "
2224
0
     << cmVersion::GetMajorVersion() << '.' << cmVersion::GetMinorVersion()
2225
0
     << "\n\n";
2226
0
}
2227
2228
std::string cmLocalUnixMakefileGenerator3::GetRecursiveMakeCall(
2229
  std::string const& makefile, std::string const& tgt)
2230
0
{
2231
  // Call make on the given file.
2232
0
  std::string cmd = cmStrCat(
2233
0
    "$(MAKE) $(MAKESILENT) -f ",
2234
0
    this->ConvertToOutputFormat(makefile, cmOutputConverter::SHELL), ' ');
2235
2236
0
  cmGlobalUnixMakefileGenerator3* gg =
2237
0
    static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
2238
  // Pass down verbosity level.
2239
0
  if (!gg->MakeSilentFlag.empty()) {
2240
0
    cmd += gg->MakeSilentFlag;
2241
0
    cmd += " ";
2242
0
  }
2243
2244
  // Most unix makes will pass the command line flags to make down to
2245
  // sub-invoked makes via an environment variable.  However, some
2246
  // makes do not support that, so you have to pass the flags
2247
  // explicitly.
2248
0
  if (gg->PassMakeflags) {
2249
0
    cmd += "-$(MAKEFLAGS) ";
2250
0
  }
2251
2252
  // Add the target.
2253
0
  if (!tgt.empty()) {
2254
    // The make target is always relative to the top of the build tree.
2255
0
    std::string tgt2 = this->MaybeRelativeToTopBinDir(tgt);
2256
2257
    // The target may have been written with windows paths.
2258
0
    cmSystemTools::ConvertToOutputSlashes(tgt2);
2259
2260
    // Escape one extra time if the make tool requires it.
2261
0
    if (this->MakeCommandEscapeTargetTwice) {
2262
0
      tgt2 = this->EscapeForShell(tgt2, true, false);
2263
0
    }
2264
2265
    // The target name is now a string that should be passed verbatim
2266
    // on the command line.
2267
0
    cmd += this->EscapeForShell(tgt2, true, false);
2268
0
  }
2269
0
  return cmd;
2270
0
}
2271
2272
void cmLocalUnixMakefileGenerator3::WriteDivider(std::ostream& os)
2273
0
{
2274
0
  os << "#======================================"
2275
0
        "=======================================\n";
2276
0
}
2277
2278
void cmLocalUnixMakefileGenerator3::WriteCMakeArgument(std::ostream& os,
2279
                                                       std::string const& s)
2280
0
{
2281
  // Write the given string to the stream with escaping to get it back
2282
  // into CMake through the lexical scanner.
2283
0
  os << '"';
2284
0
  for (char c : s) {
2285
0
    if (c == '\\') {
2286
0
      os << "\\\\";
2287
0
    } else if (c == '"') {
2288
0
      os << "\\\"";
2289
0
    } else {
2290
0
      os << c;
2291
0
    }
2292
0
  }
2293
0
  os << '"';
2294
0
}
2295
2296
std::string cmLocalUnixMakefileGenerator3::ConvertToQuotedOutputPath(
2297
  std::string const& p, bool useWatcomQuote)
2298
0
{
2299
  // Split the path into its components.
2300
0
  std::vector<std::string> components;
2301
0
  cmSystemTools::SplitPath(p, components);
2302
2303
  // Open the quoted result.
2304
0
  std::string result;
2305
0
  if (useWatcomQuote) {
2306
#if defined(_WIN32) && !defined(__CYGWIN__)
2307
    result = "'";
2308
#else
2309
0
    result = "\"'";
2310
0
#endif
2311
0
  } else {
2312
0
    result = "\"";
2313
0
  }
2314
2315
  // Return an empty path if there are no components.
2316
0
  if (!components.empty()) {
2317
    // Choose a slash direction and fix root component.
2318
0
    char const* slash = "/";
2319
#if defined(_WIN32) && !defined(__CYGWIN__)
2320
    if (!cmSystemTools::GetForceUnixPaths()) {
2321
      slash = "\\";
2322
      for (char& i : components[0]) {
2323
        if (i == '/') {
2324
          i = '\\';
2325
        }
2326
      }
2327
    }
2328
#endif
2329
2330
    // Begin the quoted result with the root component.
2331
0
    result += components[0];
2332
2333
0
    if (components.size() > 1) {
2334
      // Now add the rest of the components separated by the proper slash
2335
      // direction for this platform.
2336
0
      auto compEnd = std::remove(components.begin() + 1, components.end() - 1,
2337
0
                                 std::string());
2338
0
      auto compStart = components.begin() + 1;
2339
0
      result += cmJoin(cmMakeRange(compStart, compEnd), slash);
2340
      // Only the last component can be empty to avoid double slashes.
2341
0
      result += slash;
2342
0
      result += components.back();
2343
0
    }
2344
0
  }
2345
2346
  // Close the quoted result.
2347
0
  if (useWatcomQuote) {
2348
#if defined(_WIN32) && !defined(__CYGWIN__)
2349
    result += "'";
2350
#else
2351
0
    result += "'\"";
2352
0
#endif
2353
0
  } else {
2354
0
    result += "\"";
2355
0
  }
2356
2357
0
  return result;
2358
0
}
2359
2360
cmLocalUnixMakefileGenerator3::ImplicitDependLanguageMap const&
2361
cmLocalUnixMakefileGenerator3::GetImplicitDepends(
2362
  cmGeneratorTarget const* tgt, cmDependencyScannerKind scanner)
2363
0
{
2364
0
  return this->ImplicitDepends[tgt->GetName()][scanner];
2365
0
}
2366
2367
void cmLocalUnixMakefileGenerator3::AddImplicitDepends(
2368
  cmGeneratorTarget const* tgt, std::string const& lang,
2369
  std::string const& obj, std::string const& src,
2370
  cmDependencyScannerKind scanner)
2371
0
{
2372
0
  this->ImplicitDepends[tgt->GetName()][scanner][lang][obj].push_back(src);
2373
0
}
2374
2375
void cmLocalUnixMakefileGenerator3::CreateCDCommand(
2376
  std::vector<std::string>& commands, std::string const& tgtDir,
2377
  std::string const& relDir)
2378
0
{
2379
  // do we need to cd?
2380
0
  if (tgtDir == relDir) {
2381
0
    return;
2382
0
  }
2383
2384
  // In a Windows shell we must change drive letter too.  The shell
2385
  // used by NMake and Borland make does not support "cd /d" so this
2386
  // feature simply cannot work with them (Borland make does not even
2387
  // support changing the drive letter with just "d:").
2388
0
  char const* cd_cmd = this->IsMinGWMake() ? "cd /d " : "cd ";
2389
2390
0
  cmGlobalUnixMakefileGenerator3* gg =
2391
0
    static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
2392
0
  if (!gg->UnixCD) {
2393
    // On Windows we must perform each step separately and then change
2394
    // back because the shell keeps the working directory between
2395
    // commands.
2396
0
    std::string cmd =
2397
0
      cmStrCat(cd_cmd, this->ConvertToOutputForExisting(tgtDir));
2398
0
    commands.insert(commands.begin(), cmd);
2399
2400
    // Change back to the starting directory.
2401
0
    cmd = cmStrCat(cd_cmd, this->ConvertToOutputForExisting(relDir));
2402
0
    commands.push_back(std::move(cmd));
2403
0
  } else {
2404
    // On UNIX we must construct a single shell command to change
2405
    // directory and build because make resets the directory between
2406
    // each command.
2407
0
    std::string outputForExisting = this->ConvertToOutputForExisting(tgtDir);
2408
0
    std::string prefix = cmStrCat(cd_cmd, outputForExisting, " && ");
2409
0
    std::transform(commands.begin(), commands.end(), commands.begin(),
2410
0
                   [&prefix](std::string const& s) { return prefix + s; });
2411
0
  }
2412
0
}