cmLocalUnixMakefileGenerator3.cxx 72.7 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 "cmLocalGenerator.h"
Ken Martin's avatar
Ken Martin committed
21
#include "cmMakefile.h"
22
#include "cmMakefileTargetGenerator.h"
23
#include "cmOutputConverter.h"
24
#include "cmRulePlaceholderExpander.h"
Ken Martin's avatar
Ken Martin committed
25
#include "cmSourceFile.h"
26 27
#include "cmState.h"
#include "cmStateDirectory.h"
28
#include "cmStateSnapshot.h"
29
#include "cmStateTypes.h"
30
#include "cmSystemTools.h"
31
#include "cmVersion.h"
32
#include "cmake.h"
Ken Martin's avatar
Ken Martin committed
33 34 35 36 37

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

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

55 56
private:
  const char* Data;
57 58 59 60 61 62 63 64 65 66
  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;
67 68
      }
    }
69 70
    return os;
  }
71 72
};

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

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

cmLocalUnixMakefileGenerator3::~cmLocalUnixMakefileGenerator3()
{
}

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

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

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

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

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

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

185 186
void cmLocalUnixMakefileGenerator3::GetIndividualFileTargets(
  std::vector<std::string>& targets)
187
{
188 189
  std::map<std::string, LocalObjectInfo> localObjectFiles;
  this->GetLocalObjectFiles(localObjectFiles);
190 191 192 193 194 195
  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) {
196
      targets.push_back(base + ".i");
197
    }
198

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

205 206 207 208 209 210 211 212
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);
213
  cmGeneratedFileStream ruleFileStream(
214
    ruleFileNameFull, false, this->GlobalGenerator->GetMakefileEncoding());
215
  if (!ruleFileStream) {
216
    return;
217
  }
218
  // always write the top makefile
219
  if (!this->IsRootMakefile()) {
220
    ruleFileStream.SetCopyIfDifferent(true);
221
  }
222

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

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

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

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

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

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

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

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

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

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

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

299 300 301
void cmLocalUnixMakefileGenerator3::WriteObjectConvenienceRule(
  std::ostream& ruleFileStream, const char* comment, const char* output,
  LocalObjectInfo const& info)
302
{
303 304 305 306
  // 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;
307
  if (info.HasSourceExtension) {
308 309 310 311 312 313 314 315 316 317 318 319 320
    // 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;
    depends.push_back(output);
321
    std::vector<std::string> no_commands;
Daniel Pfeifer's avatar
Daniel Pfeifer committed
322
    this->WriteMakeRule(ruleFileStream, nullptr, outNoExt, depends,
Daniel Pfeifer's avatar
Daniel Pfeifer committed
323
                        no_commands, true, true);
324
    inHelp = false;
325
  }
326

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

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

347 348
void cmLocalUnixMakefileGenerator3::WriteLocalMakefileTargets(
  std::ostream& ruleFileStream, std::set<std::string>& emitted)
Ken Martin's avatar
Ken Martin committed
349
{
350 351 352 353 354
  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
355
  const std::vector<cmGeneratorTarget*>& targets = this->GetGeneratorTargets();
356
  std::string localName;
357 358 359 360 361 362 363 364
  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());
365 366

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

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

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

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

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

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

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

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

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

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

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

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

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

499 500 501 502
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
503
{
504
  // Make sure there is a target.
505
  if (target.empty()) {
506 507
    cmSystemTools::Error("No target for WriteMakeRule! called with comment: ",
                         comment);
508
    return;
509
  }
510 511 512 513

  std::string replace;

  // Write the comment describing the rule in the makefile.
514
  if (comment) {
515 516 517
    replace = comment;
    std::string::size_type lpos = 0;
    std::string::size_type rpos;
518 519 520
    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
521
    }
522 523
    os << "# " << replace.substr(lpos) << "\n";
  }
524 525

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

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

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

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

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

572 573
std::string cmLocalUnixMakefileGenerator3::MaybeConvertWatcomShellCommand(
  std::string const& cmd)
574
{
575
  if (this->IsWatcomWMake() && cmSystemTools::FileIsFullPath(cmd) &&
576
      cmd.find_first_of("( )") != std::string::npos) {
577 578 579 580
    // 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;
581
    if (cmSystemTools::GetShortPath(cmd, scmd)) {
582
      return this->ConvertToOutputFormat(scmd, cmOutputConverter::SHELL);
583
    }
584
  }
585
  return std::string();
586 587
}

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

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

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

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

668 669 670 671 672 673
  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.
674 675 676
  this->WriteMakeRule(makefileStream,
                      "Disable implicit rules so canonical targets will work.",
                      ".SUFFIXES", no_depends, no_commands, false);
677

678 679
  if (!this->IsNMake() && !this->IsWatcomWMake() &&
      !this->BorlandMakeCurlyHack) {
680
    // turn off RCS and SCCS automatic stuff from gmake
681 682
    makefileStream
      << "# Remove some rules from gmake that .SUFFIXES does not remove.\n"
683
      << "SUFFIXES =\n\n";
684
  }
685 686 687
  // Add a fake suffix to keep HP happy.  Must be max 32 chars for SGI make.
  std::vector<std::string> depends;
  depends.push_back(".hpux_make_needs_suffix_list");
Daniel Pfeifer's avatar
Daniel Pfeifer committed
688
  this->WriteMakeRule(makefileStream, nullptr, ".SUFFIXES", depends,
Daniel Pfeifer's avatar
Daniel Pfeifer committed
689
                      no_commands, false);
690
  if (this->IsWatcomWMake()) {
691 692 693
    // 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).
694
    /* clang-format off */
695 696 697 698 699
    makefileStream <<
      "\n"
      ".ERASE\n"
      "\n"
      ;
700
    /* clang-format on */
701 702
  }
  if (this->Makefile->IsOn("CMAKE_VERBOSE_MAKEFILE")) {
703
    /* clang-format off */
704 705 706 707
    makefileStream
      << "# Produce verbose output by default.\n"
      << "VERBOSE = 1\n"
      << "\n";
708
    /* clang-format on */
709 710
  }
  if (this->IsWatcomWMake()) {
711
    /* clang-format off */
712 713 714 715
    makefileStream <<
      "!ifndef VERBOSE\n"
      ".SILENT\n"
      "!endif\n"
716
      "\n"
717
      ;
718
    /* clang-format on */
719
  } else {
720 721 722 723 724 725
    // 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.
726 727
    this->WriteMakeRule(makefileStream,
                        "Suppress display of executed commands.",
728 729
                        "$(VERBOSE).SILENT", no_depends, no_commands, false);
  }
730

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

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

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

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

  // Write special "cmake_check_build_system" target to run cmake with
  // the --check-build-system flag.
762 763
  if (!this->GlobalGenerator->GlobalSettingIsOn(
        "CMAKE_SUPPRESS_REGENERATION")) {
764
    // Build command to run CMake to check if anything needs regenerating.
765 766 767 768 769 770 771 772
    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);
    }
773 774 775 776 777
    std::string cmakefileName = cmake::GetCMakeFilesDirectoryPostSlash();
    cmakefileName += "Makefile.cmake";
    std::string runRule =
      "$(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)";
    runRule += " --check-build-system ";
778 779
    runRule +=
      this->ConvertToOutputFormat(cmakefileName, cmOutputConverter::SHELL);
780
    runRule += " 0";
781

782
    std::vector<std::string> no_depends;
783
    commands.push_back(std::move(runRule));
784 785
    if (!this->IsRootMakefile()) {
      this->CreateCDCommand(commands, this->GetBinaryDirectory(),
786
                            this->GetCurrentBinaryDirectory());
787
    }
788 789 790 791 792 793 794 795
    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
796 797 798
  }
}

799 800 801
void cmLocalUnixMakefileGenerator3::WriteConvenienceRule(
  std::ostream& ruleFileStream, const std::string& realTarget,
  const std::string& helpTarget)
Ken Martin's avatar
Ken Martin committed
802 803
{
  // A rule is only needed if the names are different.
804
  if (realTarget != helpTarget) {
Ken Martin's avatar
Ken Martin committed
805 806 807 808 809 810 811 812 813
    // 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.",
814
                        helpTarget, depends, no_commands, true);
815
  }
Ken Martin's avatar
Ken Martin committed
816 817
}

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

Marc Chevrier's avatar
Marc Chevrier committed
826 827
void cmLocalUnixMakefileGenerator3::AppendFlags(
  std::string& flags, const std::string& newFlags) const
828
{
829
  if (this->IsWatcomWMake() && !newFlags.empty()) {
830
    std::string newf = newFlags;
831
    if (newf.find("\\\"") != std::string::npos) {
832
      cmSystemTools::ReplaceString(newf, "\\\"", "\"");
833
      this->cmLocalGenerator::AppendFlags(flags, newf);
834 835
      return;
    }
836
  }
837 838 839
  this->cmLocalGenerator::AppendFlags(flags, newFlags);
}

840
void cmLocalUnixMakefileGenerator3::AppendFlags(std::string& flags,
Marc Chevrier's avatar
Marc Chevrier committed
841
                                                const char* newFlags) const
842 843 844 845
{
  this->cmLocalGenerator::AppendFlags(flags, newFlags);
}

846 847
void cmLocalUnixMakefileGenerator3::AppendRuleDepend(
  std::vector<std::string>& depends, const char* ruleFileName)
848 849 850
{
  // Add a dependency on the rule file itself unless an option to skip
  // it is specifically enabled by the user or project.
851
  const char* nodep =
Ken Martin's avatar
Ken Martin committed
852
    this->Makefile->GetDefinition("CMAKE_SKIP_RULE_DEPENDENCY");
853
  if (!nodep || cmSystemTools::IsOff(nodep)) {
Ken Martin's avatar