cmLocalUnixMakefileGenerator3.cxx 72.4 KB
Newer Older
1 2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
Ken Martin's avatar
Ken Martin committed
3 4
#include "cmLocalUnixMakefileGenerator3.h"

5 6
#include "cmsys/FStream.hxx"
#include "cmsys/Terminal.h"
7
#include <algorithm>
8
#include <memory> // IWYU pragma: keep
9 10 11 12
#include <sstream>
#include <stdio.h>
#include <utility>

13 14 15
#include "cmAlgorithms.h"
#include "cmCustomCommandGenerator.h"
#include "cmFileTimeComparison.h"
Ken Martin's avatar
Ken Martin committed
16
#include "cmGeneratedFileStream.h"
17 18
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
19
#include "cmGlobalUnixMakefileGenerator3.h"
20
#include "cmListFileCache.h"
21
#include "cmLocalGenerator.h"
Ken Martin's avatar
Ken Martin committed
22
#include "cmMakefile.h"
23
#include "cmMakefileTargetGenerator.h"
24
#include "cmOutputConverter.h"
25
#include "cmRulePlaceholderExpander.h"
Ken Martin's avatar
Ken Martin committed
26
#include "cmSourceFile.h"
27 28
#include "cmState.h"
#include "cmStateDirectory.h"
29
#include "cmStateSnapshot.h"
30
#include "cmStateTypes.h"
31
#include "cmSystemTools.h"
32
#include "cmVersion.h"
33
#include "cmake.h"
Ken Martin's avatar
Ken Martin committed
34 35 36 37 38

// Include dependency scanners for supported languages.  Only the
// C/C++ scanner is needed for bootstrapping CMake.
#include "cmDependsC.h"
#ifdef CMAKE_BUILD_WITH_CMAKE
39 40
#  include "cmDependsFortran.h"
#  include "cmDependsJava.h"
Ken Martin's avatar
Ken Martin committed
41 42
#endif

43 44 45 46
// Escape special characters in Makefile dependency lines
class cmMakeSafe
{
public:
47 48 49 50 51 52 53 54 55
  cmMakeSafe(const char* s)
    : Data(s)
  {
  }
  cmMakeSafe(std::string const& s)
    : Data(s.c_str())
  {
  }

56 57
private:
  const char* Data;
58 59 60 61 62 63 64 65 66 67
  friend std::ostream& operator<<(std::ostream& os, cmMakeSafe const& self)
  {
    for (const char* c = self.Data; *c; ++c) {
      switch (*c) {
        case '=':
          os << "$(EQUALS)";
          break;
        default:
          os << *c;
          break;
68 69
      }
    }
70 71
    return os;
  }
72 73
};

74 75 76 77
// Helper function used below.
static std::string cmSplitExtension(std::string const& in, std::string& base)
{
  std::string ext;
78
  std::string::size_type dot_pos = in.rfind('.');
79
  if (dot_pos != std::string::npos) {
80
    // Remove the extension first in case &base == &in.
81
    ext = in.substr(dot_pos);
82
    base = in.substr(0, dot_pos);
83
  } else {
84
    base = in;
85
  }
86 87 88
  return ext;
}

89 90
cmLocalUnixMakefileGenerator3::cmLocalUnixMakefileGenerator3(
  cmGlobalGenerator* gg, cmMakefile* mf)
91
  : cmLocalCommonGenerator(gg, mf, mf->GetCurrentBinaryDirectory())
Ken Martin's avatar
Ken Martin committed
92
{
93
  this->MakefileVariableSize = 0;
94
  this->ColorMakefile = false;
95 96
  this->SkipPreprocessedSourceRules = false;
  this->SkipAssemblySourceRules = false;
97
  this->MakeCommandEscapeTargetTwice = false;
98
  this->BorlandMakeCurlyHack = false;
Ken Martin's avatar
Ken Martin committed
99 100
}

wahikihiki's avatar
wahikihiki committed
101
cmLocalUnixMakefileGenerator3::~cmLocalUnixMakefileGenerator3() = default;
Ken Martin's avatar
Ken Martin committed
102

103
void cmLocalUnixMakefileGenerator3::Generate()
Ken Martin's avatar
Ken Martin committed
104
{
105
  // Record whether some options are enabled to avoid checking many
106
  // times later.
107
  if (!this->GetGlobalGenerator()->GetCMakeInstance()->GetIsInTryCompile()) {
108
    this->ColorMakefile = this->Makefile->IsOn("CMAKE_COLOR_MAKEFILE");
109
  }
110 111 112 113
  this->SkipPreprocessedSourceRules =
    this->Makefile->IsOn("CMAKE_SKIP_PREPROCESSED_SOURCE_RULES");
  this->SkipAssemblySourceRules =
    this->Makefile->IsOn("CMAKE_SKIP_ASSEMBLY_SOURCE_RULES");
114

Ken Martin's avatar
Ken Martin committed
115
  // Generate the rule files for each target.
116
  const std::vector<cmGeneratorTarget*>& targets = this->GetGeneratorTargets();
117 118
  cmGlobalUnixMakefileGenerator3* gg =
    static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
119 120
  for (cmGeneratorTarget* target : targets) {
    if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
121
      continue;
122
    }
123
    std::unique_ptr<cmMakefileTargetGenerator> tg(
124
      cmMakefileTargetGenerator::New(target));
125
    if (tg) {
126
      tg->WriteRuleFiles();
127
      gg->RecordTargetProgress(tg.get());
Ken Martin's avatar
Ken Martin committed
128
    }
129
  }
Ken Martin's avatar
Ken Martin committed
130

Ken Martin's avatar
Ken Martin committed
131 132
  // write the local Makefile
  this->WriteLocalMakefile();
133

Ken Martin's avatar
Ken Martin committed
134 135
  // Write the cmake file with information for this directory.
  this->WriteDirectoryInformationFile();
136 137
}

138 139 140 141
void cmLocalUnixMakefileGenerator3::ComputeHomeRelativeOutputPath()
{
  // Compute the path to use when referencing the current output
  // directory from the top output directory.
142
  this->HomeRelativeOutputPath = this->MaybeConvertToRelativePath(
143
    this->GetBinaryDirectory(), this->GetCurrentBinaryDirectory());
144
  if (this->HomeRelativeOutputPath == ".") {
145
    this->HomeRelativeOutputPath.clear();
146 147
  }
  if (!this->HomeRelativeOutputPath.empty()) {
148
    this->HomeRelativeOutputPath += "/";
149
  }
150 151
}

152 153
void cmLocalUnixMakefileGenerator3::GetLocalObjectFiles(
  std::map<std::string, LocalObjectInfo>& localObjectFiles)
154
{
155
  const std::vector<cmGeneratorTarget*>& targets = this->GetGeneratorTargets();
156
  for (cmGeneratorTarget* gt : targets) {
157
    if (gt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
158
      continue;
159
    }
160
    std::vector<cmSourceFile const*> objectSources;
161 162
    gt->GetObjectSources(
      objectSources, this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
163
    // Compute full path to object file directory for this target.
164
    std::string dir;
165
    dir += gt->LocalGenerator->GetCurrentBinaryDirectory();
166
    dir += "/";
167
    dir += this->GetTargetDirectory(gt);
168
    dir += "/";
169
    // Compute the name of each object file.
170
    for (cmSourceFile const* sf : objectSources) {
171
      bool hasSourceExtension = true;
172 173
      std::string objectName =
        this->GetObjectFileNameWithoutTarget(*sf, dir, &hasSourceExtension);
174
      if (cmSystemTools::FileIsFullPath(objectName)) {
175
        objectName = cmSystemTools::GetFilenameName(objectName);
176
      }
177 178
      LocalObjectInfo& info = localObjectFiles[objectName];
      info.HasSourceExtension = hasSourceExtension;
179
      info.emplace_back(gt, sf->GetLanguage());
180
    }
181
  }
182 183
}

184 185
void cmLocalUnixMakefileGenerator3::GetIndividualFileTargets(
  std::vector<std::string>& targets)
186
{
187 188
  std::map<std::string, LocalObjectInfo> localObjectFiles;
  this->GetLocalObjectFiles(localObjectFiles);
189 190 191 192 193 194
  for (auto const& localObjectFile : localObjectFiles) {
    targets.push_back(localObjectFile.first);

    std::string::size_type dot_pos = localObjectFile.first.rfind(".");
    std::string base = localObjectFile.first.substr(0, dot_pos);
    if (localObjectFile.second.HasPreprocessRule) {
195
      targets.push_back(base + ".i");
196
    }
197

198
    if (localObjectFile.second.HasAssembleRule) {
199 200
      targets.push_back(base + ".s");
    }
201
  }
202 203
}

204 205 206 207 208 209 210 211
void cmLocalUnixMakefileGenerator3::WriteLocalMakefile()
{
  // generate the includes
  std::string ruleFileName = "Makefile";

  // Open the rule file.  This should be copy-if-different because the
  // rules may depend on this file itself.
  std::string ruleFileNameFull = this->ConvertToFullPath(ruleFileName);
212
  cmGeneratedFileStream ruleFileStream(
213
    ruleFileNameFull, false, this->GlobalGenerator->GetMakefileEncoding());
214
  if (!ruleFileStream) {
215
    return;
216
  }
217
  // always write the top makefile
218
  if (!this->IsRootMakefile()) {
219
    ruleFileStream.SetCopyIfDifferent(true);
220
  }
221

222 223
  // write the all rules
  this->WriteLocalAllRules(ruleFileStream);
224

225 226
  // only write local targets unless at the top Keep track of targets already
  // listed.
227
  std::set<std::string> emittedTargets;
228
  if (!this->IsRootMakefile()) {
229 230
    // write our targets, and while doing it collect up the object
    // file rules
231 232 233
    this->WriteLocalMakefileTargets(ruleFileStream, emittedTargets);
  } else {
    cmGlobalUnixMakefileGenerator3* gg =
234
      static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
235 236
    gg->WriteConvenienceRules(ruleFileStream, emittedTargets);
  }
237

238 239
  bool do_preprocess_rules = this->GetCreatePreprocessedSourceRules();
  bool do_assembly_rules = this->GetCreateAssemblySourceRules();
240

241 242 243
  std::map<std::string, LocalObjectInfo> localObjectFiles;
  this->GetLocalObjectFiles(localObjectFiles);

244 245
  // now write out the object rules
  // for each object file name
246
  for (auto& localObjectFile : localObjectFiles) {
247
    // Add a convenience rule for building the object file.
248
    this->WriteObjectConvenienceRule(
249 250
      ruleFileStream, "target to build an object file", localObjectFile.first,
      localObjectFile.second);
251

252 253
    // Check whether preprocessing and assembly rules make sense.
    // They make sense only for C and C++ sources.
254 255 256
    bool lang_has_preprocessor = false;
    bool lang_has_assembly = false;

257 258 259
    for (LocalObjectEntry const& entry : localObjectFile.second) {
      if (entry.Language == "C" || entry.Language == "CXX" ||
          entry.Language == "CUDA" || entry.Language == "Fortran") {
260
        // Right now, C, C++, Fortran and CUDA have both a preprocessor and the
261 262 263
        // ability to generate assembly code
        lang_has_preprocessor = true;
        lang_has_assembly = true;
264
        break;
265
      }
266
    }
267

268
    // Add convenience rules for preprocessed and assembly files.
269
    if (lang_has_preprocessor && do_preprocess_rules) {
270 271
      std::string::size_type dot_pos = localObjectFile.first.rfind(".");
      std::string base = localObjectFile.first.substr(0, dot_pos);
272 273 274
      this->WriteObjectConvenienceRule(ruleFileStream,
                                       "target to preprocess a source file",
                                       (base + ".i"), localObjectFile.second);
275
      localObjectFile.second.HasPreprocessRule = true;
276
    }
277

278
    if (lang_has_assembly && do_assembly_rules) {
279 280
      std::string::size_type dot_pos = localObjectFile.first.rfind(".");
      std::string base = localObjectFile.first.substr(0, dot_pos);
281
      this->WriteObjectConvenienceRule(
282
        ruleFileStream, "target to generate assembly for a file",
283
        (base + ".s"), localObjectFile.second);
284
      localObjectFile.second.HasAssembleRule = true;
285
    }
286
  }
287 288

  // add a help target as long as there isn;t a real target named help
289 290
  if (emittedTargets.insert("help").second) {
    cmGlobalUnixMakefileGenerator3* gg =
291
      static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
292 293
    gg->WriteHelpRule(ruleFileStream, this);
  }
294 295 296

  this->WriteSpecialTargetsBottom(ruleFileStream);
}
Ken Martin's avatar
Ken Martin committed
297

298
void cmLocalUnixMakefileGenerator3::WriteObjectConvenienceRule(
299
  std::ostream& ruleFileStream, const char* comment, const std::string& output,
300
  LocalObjectInfo const& info)
301
{
302 303 304 305
  // If the rule includes the source file extension then create a
  // version that has the extension removed.  The help should include
  // only the version without source extension.
  bool inHelp = true;
306
  if (info.HasSourceExtension) {
307 308 309 310 311 312 313 314 315 316 317 318
    // Remove the last extension.  This should be kept.
    std::string outBase1 = output;
    std::string outExt1 = cmSplitExtension(outBase1, outBase1);

    // Now remove the source extension and put back the last
    // extension.
    std::string outNoExt;
    cmSplitExtension(outBase1, outNoExt);
    outNoExt += outExt1;

    // Add a rule to drive the rule below.
    std::vector<std::string> depends;
wahikihiki's avatar
wahikihiki committed
319
    depends.emplace_back(output);
320
    std::vector<std::string> no_commands;
Daniel Pfeifer's avatar
Daniel Pfeifer committed
321
    this->WriteMakeRule(ruleFileStream, nullptr, outNoExt, depends,
Daniel Pfeifer's avatar
Daniel Pfeifer committed
322
                        no_commands, true, true);
323
    inHelp = false;
324
  }
325

326 327
  // Recursively make the rule for each target using the object file.
  std::vector<std::string> commands;
328 329
  for (LocalObjectEntry const& t : info) {
    std::string tgtMakefileName = this->GetRelativeTargetDirectory(t.Target);
330 331 332 333 334
    std::string targetName = tgtMakefileName;
    tgtMakefileName += "/build.make";
    targetName += "/";
    targetName += output;
    commands.push_back(
335
      this->GetRecursiveMakeCall(tgtMakefileName, targetName));
336 337
  }
  this->CreateCDCommand(commands, this->GetBinaryDirectory(),
338
                        this->GetCurrentBinaryDirectory());
339 340 341

  // Write the rule to the makefile.
  std::vector<std::string> no_depends;
342 343
  this->WriteMakeRule(ruleFileStream, comment, output, no_depends, commands,
                      true, inHelp);
344 345
}

346 347
void cmLocalUnixMakefileGenerator3::WriteLocalMakefileTargets(
  std::ostream& ruleFileStream, std::set<std::string>& emitted)
Ken Martin's avatar
Ken Martin committed
348
{
349 350 351 352 353
  std::vector<std::string> depends;
  std::vector<std::string> commands;

  // for each target we just provide a rule to cd up to the top and do a make
  // on the target
354
  const std::vector<cmGeneratorTarget*>& targets = this->GetGeneratorTargets();
355
  std::string localName;
356 357 358 359 360 361 362 363
  for (cmGeneratorTarget* target : targets) {
    if ((target->GetType() == cmStateEnums::EXECUTABLE) ||
        (target->GetType() == cmStateEnums::STATIC_LIBRARY) ||
        (target->GetType() == cmStateEnums::SHARED_LIBRARY) ||
        (target->GetType() == cmStateEnums::MODULE_LIBRARY) ||
        (target->GetType() == cmStateEnums::OBJECT_LIBRARY) ||
        (target->GetType() == cmStateEnums::UTILITY)) {
      emitted.insert(target->GetName());
364 365

      // for subdirs add a rule to build this specific target by name.
366
      localName = this->GetRelativeTargetDirectory(target);
367 368 369
      localName += "/rule";
      commands.clear();
      depends.clear();
370

371
      // Build the target for this pass.
372
      std::string makefile2 = "CMakeFiles/";
373
      makefile2 += "Makefile2";
374
      commands.push_back(this->GetRecursiveMakeCall(makefile2, localName));
375
      this->CreateCDCommand(commands, this->GetBinaryDirectory(),
376
                            this->GetCurrentBinaryDirectory());
377
      this->WriteMakeRule(ruleFileStream, "Convenience name for target.",
Stephen Kelly's avatar
Stephen Kelly committed
378
                          localName, depends, commands, true);
379

380
      // Add a target with the canonical name (no prefix, suffix or path).
381
      if (localName != target->GetName()) {
382 383 384
        commands.clear();
        depends.push_back(localName);
        this->WriteMakeRule(ruleFileStream, "Convenience name for target.",
385
                            target->GetName(), depends, commands, true);
386
      }
387 388

      // Add a fast rule to build the target
389
      std::string makefileName = this->GetRelativeTargetDirectory(target);
390
      makefileName += "/build.make";
391
      // make sure the makefile name is suitable for a makefile
392
      std::string makeTargetName = this->GetRelativeTargetDirectory(target);
393
      makeTargetName += "/build";
394
      localName = target->GetName();
395 396 397
      localName += "/fast";
      depends.clear();
      commands.clear();
398
      commands.push_back(
399
        this->GetRecursiveMakeCall(makefileName, makeTargetName));
400
      this->CreateCDCommand(commands, this->GetBinaryDirectory(),
401
                            this->GetCurrentBinaryDirectory());
402
      this->WriteMakeRule(ruleFileStream, "fast build rule for target.",
Stephen Kelly's avatar
Stephen Kelly committed
403
                          localName, depends, commands, true);
404 405 406

      // Add a local name for the rule to relink the target before
      // installation.
407 408
      if (target->NeedRelinkBeforeInstall(this->ConfigName)) {
        makeTargetName = this->GetRelativeTargetDirectory(target);
409
        makeTargetName += "/preinstall";
410
        localName = target->GetName();
411 412 413
        localName += "/preinstall";
        depends.clear();
        commands.clear();
414
        commands.push_back(
415
          this->GetRecursiveMakeCall(makefile2, makeTargetName));
416
        this->CreateCDCommand(commands, this->GetBinaryDirectory(),
417
                              this->GetCurrentBinaryDirectory());
418 419
        this->WriteMakeRule(ruleFileStream,
                            "Manual pre-install relink rule for target.",
Stephen Kelly's avatar
Stephen Kelly committed
420
                            localName, depends, commands, true);
Ken Martin's avatar
Ken Martin committed
421 422
      }
    }
423
  }
Ken Martin's avatar
Ken Martin committed
424 425
}

Ken Martin's avatar
Ken Martin committed
426
void cmLocalUnixMakefileGenerator3::WriteDirectoryInformationFile()
Ken Martin's avatar
Ken Martin committed
427
{
428
  std::string infoFileName = this->GetCurrentBinaryDirectory();
429
  infoFileName += "/CMakeFiles";
430
  infoFileName += "/CMakeDirectoryInformation.cmake";
Ken Martin's avatar
Ken Martin committed
431 432

  // Open the output file.
433
  cmGeneratedFileStream infoFileStream(infoFileName);
434
  if (!infoFileStream) {
Ken Martin's avatar
Ken Martin committed
435
    return;
436
  }
Ken Martin's avatar
Ken Martin committed
437

438
  infoFileStream.SetCopyIfDifferent(true);
Ken Martin's avatar
Ken Martin committed
439 440 441
  // Write the do not edit header.
  this->WriteDisclaimer(infoFileStream);

442
  // Setup relative path conversion tops.
443
  /* clang-format off */
444 445
  infoFileStream
    << "# Relative path conversion top directories.\n"
446
    << "set(CMAKE_RELATIVE_PATH_TOP_SOURCE \""
447
    << this->StateSnapshot.GetDirectory().GetRelativePathTopSource()
448
    << "\")\n"
449
    << "set(CMAKE_RELATIVE_PATH_TOP_BINARY \""
450
    << this->StateSnapshot.GetDirectory().GetRelativePathTopBinary()
451 452
    << "\")\n"
    << "\n";
453
  /* clang-format on */
454

Ken Martin's avatar
Ken Martin committed
455
  // Tell the dependency scanner to use unix paths if necessary.
456
  if (cmSystemTools::GetForceUnixPaths()) {
457
    /* clang-format off */
Ken Martin's avatar
Ken Martin committed
458 459
    infoFileStream
      << "# Force unix paths in dependencies.\n"
460
      << "set(CMAKE_FORCE_UNIX_PATHS 1)\n"
Ken Martin's avatar
Ken Martin committed
461
      << "\n";
462
    /* clang-format on */
463
  }
Ken Martin's avatar
Ken Martin committed
464 465

  // Store the include regular expressions for this directory.
466 467 468 469
  infoFileStream << "\n"
                 << "# The C and CXX include file regular expressions for "
                 << "this directory.\n";
  infoFileStream << "set(CMAKE_C_INCLUDE_REGEX_SCAN ";
470 471
  cmLocalUnixMakefileGenerator3::WriteCMakeArgument(
    infoFileStream, this->Makefile->GetIncludeRegularExpression());
472 473
  infoFileStream << ")\n";
  infoFileStream << "set(CMAKE_C_INCLUDE_REGEX_COMPLAIN ";
474 475
  cmLocalUnixMakefileGenerator3::WriteCMakeArgument(
    infoFileStream, this->Makefile->GetComplainRegularExpression());
476
  infoFileStream << ")\n";
Ken Martin's avatar
Ken Martin committed
477
  infoFileStream
478
    << "set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN})\n";
479 480
  infoFileStream << "set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN "
                    "${CMAKE_C_INCLUDE_REGEX_COMPLAIN})\n";
Ken Martin's avatar
Ken Martin committed
481 482
}

483 484
std::string cmLocalUnixMakefileGenerator3::ConvertToFullPath(
  const std::string& localPath)
Ken Martin's avatar
Ken Martin committed
485
{
486
  std::string dir = this->GetCurrentBinaryDirectory();
Ken Martin's avatar
Ken Martin committed
487 488 489 490 491
  dir += "/";
  dir += localPath;
  return dir;
}

492
const std::string& cmLocalUnixMakefileGenerator3::GetHomeRelativeOutputPath()
Ken Martin's avatar
Ken Martin committed
493
{
494
  return this->HomeRelativeOutputPath;
Ken Martin's avatar
Ken Martin committed
495 496
}

497 498 499 500
void cmLocalUnixMakefileGenerator3::WriteMakeRule(
  std::ostream& os, const char* comment, const std::string& target,
  const std::vector<std::string>& depends,
  const std::vector<std::string>& commands, bool symbolic, bool in_help)
Ken Martin's avatar
Ken Martin committed
501
{
502
  // Make sure there is a target.
503
  if (target.empty()) {
504 505
    cmSystemTools::Error("No target for WriteMakeRule! called with comment: ",
                         comment);
506
    return;
507
  }
508 509 510 511

  std::string replace;

  // Write the comment describing the rule in the makefile.
512
  if (comment) {
513 514 515
    replace = comment;
    std::string::size_type lpos = 0;
    std::string::size_type rpos;
516 517 518
    while ((rpos = replace.find('\n', lpos)) != std::string::npos) {
      os << "# " << replace.substr(lpos, rpos - lpos) << "\n";
      lpos = rpos + 1;
Ken Martin's avatar
Ken Martin committed
519
    }
520 521
    os << "# " << replace.substr(lpos) << "\n";
  }
522 523

  // Construct the left hand side of the rule.
524
  std::string tgt = cmSystemTools::ConvertToOutputPath(
525
    this->MaybeConvertToRelativePath(this->GetBinaryDirectory(), target));
526

527
  const char* space = "";
528
  if (tgt.size() == 1) {
529 530 531
    // Add a space before the ":" to avoid drive letter confusion on
    // Windows.
    space = " ";
532
  }
533

534
  // Mark the rule as symbolic if requested.
535 536 537
  if (symbolic) {
    if (const char* sym =
          this->Makefile->GetDefinition("CMAKE_MAKE_SYMBOLIC_RULE")) {
538
      os << cmMakeSafe(tgt) << space << ": " << sym << "\n";
539
    }
540
  }
541

542
  // Write the rule.
543
  if (depends.empty()) {
544
    // No dependencies.  The commands will always run.
545
    os << cmMakeSafe(tgt) << space << ":\n";
546
  } else {
547 548
    // Split dependencies into multiple rule lines.  This allows for
    // very long dependency lists even on older make implementations.
549
    std::string binDir = this->GetBinaryDirectory();
550 551
    for (std::string const& depend : depends) {
      replace = depend;
552
      replace = cmSystemTools::ConvertToOutputPath(
553
        this->MaybeConvertToRelativePath(binDir, replace));
554
      os << cmMakeSafe(tgt) << space << ": " << cmMakeSafe(replace) << "\n";
555
    }
556
  }
557

558
  // Write the list of commands.
Stephen Kelly's avatar
Stephen Kelly committed
559
  os << cmWrap("\t", commands, "", "\n") << "\n";
560
  if (symbolic && !this->IsWatcomWMake()) {
561
    os << ".PHONY : " << cmMakeSafe(tgt) << "\n";
562
  }
563
  os << "\n";
564
  // Add the output to the local help if requested.
565
  if (in_help) {
566
    this->LocalHelp.push_back(target);
567
  }
568 569
}

570 571
std::string cmLocalUnixMakefileGenerator3::MaybeConvertWatcomShellCommand(
  std::string const& cmd)
572
{
573
  if (this->IsWatcomWMake() && cmSystemTools::FileIsFullPath(cmd) &&
574
      cmd.find_first_of("( )") != std::string::npos) {
575 576 577 578
    // On Watcom WMake use the windows short path for the command
    // name.  This is needed to avoid funny quoting problems on
    // lines with shell redirection operators.
    std::string scmd;
579
    if (cmSystemTools::GetShortPath(cmd, scmd)) {
580
      return this->ConvertToOutputFormat(scmd, cmOutputConverter::SHELL);
581
    }
582
  }
583
  return std::string();
584 585
}

586 587
void cmLocalUnixMakefileGenerator3::WriteMakeVariables(
  std::ostream& makefileStream)
Ken Martin's avatar
Ken Martin committed
588 589
{
  this->WriteDivider(makefileStream);
590 591
  makefileStream << "# Set environment variables for the build.\n"
                 << "\n";
592 593
  cmGlobalUnixMakefileGenerator3* gg =
    static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
594 595 596 597 598 599 600 601 602 603 604
  if (gg->DefineWindowsNULL) {
    makefileStream << "!IF \"$(OS)\" == \"Windows_NT\"\n"
                   << "NULL=\n"
                   << "!ELSE\n"
                   << "NULL=nul\n"
                   << "!ENDIF\n";
  }
  if (this->IsWindowsShell()) {
    makefileStream << "SHELL = cmd.exe\n"
                   << "\n";
  } else {
605
#if !defined(__VMS)
606
    /* clang-format off */
607 608 609 610
      makefileStream
        << "# The shell in which to execute make rules.\n"
        << "SHELL = /bin/sh\n"
        << "\n";
611
/* clang-format on */
612
#endif
613
  }
Ken Martin's avatar
Ken Martin committed
614

615 616 617
  std::string cmakeShellCommand =
    this->MaybeConvertWatcomShellCommand(cmSystemTools::GetCMakeCommand());
  if (cmakeShellCommand.empty()) {
618 619 620
    cmakeShellCommand = this->ConvertToOutputFormat(
      cmSystemTools::CollapseFullPath(cmSystemTools::GetCMakeCommand()),
      cmOutputConverter::SHELL);
621
  }
622

623
  /* clang-format off */
Ken Martin's avatar
Ken Martin committed
624 625 626
  makefileStream
    << "# The CMake executable.\n"
    << "CMAKE_COMMAND = "
627
    << cmakeShellCommand
628
    << "\n"
Ken Martin's avatar
Ken Martin committed
629 630 631 632
    << "\n";
  makefileStream
    << "# The command to remove a file.\n"
    << "RM = "
633
    << cmakeShellCommand
Ken Martin's avatar
Ken Martin committed
634 635
    << " -E remove -f\n"
    << "\n";
636 637 638 639
  makefileStream
    << "# Escaping for special characters.\n"
    << "EQUALS = =\n"
    << "\n";
Ken Martin's avatar
Ken Martin committed
640 641 642
  makefileStream
    << "# The top-level source directory on which CMake was run.\n"
    << "CMAKE_SOURCE_DIR = "
643 644
    << this->ConvertToOutputFormat(
      cmSystemTools::CollapseFullPath(this->GetSourceDirectory()),
645
                     cmOutputConverter::SHELL)
Ken Martin's avatar
Ken Martin committed
646 647 648 649
    << "\n"
    << "\n";
  makefileStream
    << "# The top-level build directory on which CMake was run.\n"
650
    << "CMAKE_BINARY_DIR = "
651 652
    << this->ConvertToOutputFormat(
      cmSystemTools::CollapseFullPath(this->GetBinaryDirectory()),
653
                     cmOutputConverter::SHELL)
Ken Martin's avatar
Ken Martin committed
654 655
    << "\n"
    << "\n";
656
  /* clang-format on */
Ken Martin's avatar
Ken Martin committed
657 658
}

659 660
void cmLocalUnixMakefileGenerator3::WriteSpecialTargetsTop(
  std::ostream& makefileStream)
Ken Martin's avatar
Ken Martin committed
661 662
{
  this->WriteDivider(makefileStream);
663 664
  makefileStream << "# Special targets provided by cmake.\n"
                 << "\n";
Ken Martin's avatar
Ken Martin committed
665

666 667 668 669 670 671
  std::vector<std::string> no_commands;
  std::vector<std::string> no_depends;

  // Special target to cleanup operation of make tool.
  // This should be the first target except for the default_target in
  // the interface Makefile.
672 673 674
  this->WriteMakeRule(makefileStream,
                      "Disable implicit rules so canonical targets will work.",
                      ".SUFFIXES", no_depends, no_commands, false);
675

676 677
  if (!this->IsNMake() && !this->IsWatcomWMake() &&
      !this->BorlandMakeCurlyHack) {
678
    // turn off RCS and SCCS automatic stuff from gmake
679 680
    makefileStream
      << "# Remove some rules from gmake that .SUFFIXES does not remove.\n"
681
      << "SUFFIXES =\n\n";
682
  }
683 684
  // Add a fake suffix to keep HP happy.  Must be max 32 chars for SGI make.
  std::vector<std::string> depends;
wahikihiki's avatar
wahikihiki committed
685
  depends.emplace_back(".hpux_make_needs_suffix_list");
Daniel Pfeifer's avatar
Daniel Pfeifer committed
686
  this->WriteMakeRule(makefileStream, nullptr, ".SUFFIXES", depends,
Daniel Pfeifer's avatar
Daniel Pfeifer committed
687
                      no_commands, false);
688
  if (this->IsWatcomWMake()) {
689 690 691
    // Switch on WMake feature, if an error or interrupt occurs during
    // makefile processing, the current target being made may be deleted
    // without prompting (the same as command line -e option).
692
    /* clang-format off */
693 694 695 696 697
    makefileStream <<
      "\n"
      ".ERASE\n"
      "\n"
      ;
698
    /* clang-format on */
699 700
  }
  if (this->Makefile->IsOn("CMAKE_VERBOSE_MAKEFILE")) {
701
    /* clang-format off */
702 703 704 705
    makefileStream
      << "# Produce verbose output by default.\n"
      << "VERBOSE = 1\n"
      << "\n";
706
    /* clang-format on */
707 708
  }
  if (this->IsWatcomWMake()) {
709
    /* clang-format off */
710 711 712 713
    makefileStream <<
      "!ifndef VERBOSE\n"
      ".SILENT\n"
      "!endif\n"
714
      "\n"
715
      ;
716
    /* clang-format on */
717
  } else {
718 719 720 721 722 723
    // Write special target to silence make output.  This must be after
    // the default target in case VERBOSE is set (which changes the
    // name).  The setting of CMAKE_VERBOSE_MAKEFILE to ON will cause a
    // "VERBOSE=1" to be added as a make variable which will change the
    // name of this special target.  This gives a make-time choice to
    // the user.
724 725
    this->WriteMakeRule(makefileStream,
                        "Suppress display of executed commands.",
726 727
                        "$(VERBOSE).SILENT", no_depends, no_commands, false);
  }
728

729 730
  // Work-around for makes that drop rules that have no dependencies
  // or commands.
731 732
  cmGlobalUnixMakefileGenerator3* gg =
    static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
733
  std::string hack = gg->GetEmptyRuleHackDepends();
734
  if (!hack.empty()) {
735
    no_depends.push_back(std::move(hack));
736
  }
737
  std::string hack_cmd = gg->GetEmptyRuleHackCommand();
738
  if (!hack_cmd.empty()) {
739
    no_commands.push_back(std::move(hack_cmd));
740
  }
741

742 743
  // Special symbolic target that never exists to force dependers to
  // run their rules.
744 745
  this->WriteMakeRule(makefileStream, "A target that is always out of date.",
                      "cmake_force", no_depends, no_commands, true);
746

747 748
  // Variables for reference by other rules.
  this->WriteMakeVariables(makefileStream);
Ken Martin's avatar
Ken Martin committed
749 750
}

751 752
void cmLocalUnixMakefileGenerator3::WriteSpecialTargetsBottom(
  std::ostream& makefileStream)
Ken Martin's avatar
Ken Martin committed
753 754
{
  this->WriteDivider(makefileStream);
755 756
  makefileStream << "# Special targets to cleanup operation of make.\n"
                 << "\n";
Ken Martin's avatar
Ken Martin committed
757 758 759

  // Write special "cmake_check_build_system" target to run cmake with
  // the --check-build-system flag.
760 761
  if (!this->GlobalGenerator->GlobalSettingIsOn(
        "CMAKE_SUPPRESS_REGENERATION")) {
762
    // Build command to run CMake to check if anything needs regenerating.
763 764 765 766 767 768 769 770
    std::vector<std::string> commands;
    cmake* cm = this->GlobalGenerator->GetCMakeInstance();
    if (cm->DoWriteGlobVerifyTarget()) {
      std::string rescanRule = "$(CMAKE_COMMAND) -P ";
      rescanRule += this->ConvertToOutputFormat(cm->GetGlobVerifyScript(),
                                                cmOutputConverter::SHELL);
      commands.push_back(rescanRule);
    }
771
    std::string cmakefileName = "CMakeFiles/";
772 773
    cmakefileName += "Makefile.cmake";
    std::string runRule =
774
      "$(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)";
775
    runRule += " --check-build-system ";
776 777
    runRule +=
      this->ConvertToOutputFormat(cmakefileName, cmOutputConverter::SHELL);
778
    runRule += " 0";
779

780
    std::vector<std::string> no_depends;
781
    commands.push_back(std::move(runRule));
782 783
    if (!this->IsRootMakefile()) {
      this->CreateCDCommand(commands, this->GetBinaryDirectory(),
784
                            this->GetCurrentBinaryDirectory());
785
    }
786 787 788 789 790 791 792 793
    this->WriteMakeRule(makefileStream,
                        "Special rule to run CMake to check the build system "
                        "integrity.\n"
                        "No rule that depends on this can have "
                        "commands that come from listfiles\n"
                        "because they might be regenerated.",
                        "cmake_check_build_system", no_depends, commands,
                        true);
Ken Martin's avatar
Ken Martin committed
794 795 796
  }
}

797 798 799
void cmLocalUnixMakefileGenerator3::WriteConvenienceRule(
  std::ostream& ruleFileStream, const std::string& realTarget,
  const std::string& helpTarget)
Ken Martin's avatar
Ken Martin committed
800 801
{
  // A rule is only needed if the names are different.
802
  if (realTarget != helpTarget) {
Ken Martin's avatar
Ken Martin committed
803 804 805 806 807 808 809 810 811
    // The helper target depends on the real target.
    std::vector<std::string> depends;
    depends.push_back(realTarget);

    // There are no commands.
    std::vector<std::string> no_commands;

    // Write the rule.
    this->WriteMakeRule(ruleFileStream, "Convenience name for target.",
812
                        helpTarget, depends, no_commands, true);
813
  }
Ken Martin's avatar
Ken Martin committed
814 815
}

816 817
std::string cmLocalUnixMakefileGenerator3::GetRelativeTargetDirectory(
  cmGeneratorTarget* target)
Ken Martin's avatar
Ken Martin committed
818
{
819
  std::string dir = this->HomeRelativeOutputPath;
820
  dir += this->GetTargetDirectory(target);
821
  return dir;
822 823
}

Marc Chevrier's avatar
Marc Chevrier committed
824 825
void cmLocalUnixMakefileGenerator3::AppendFlags(
  std::string& flags, const std::string& newFlags) const
826
{
827
  if (this->IsWatcomWMake() && !newFlags.empty()) {
828
    std::string newf = newFlags;
829
    if (newf.find("\\\"") !=