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