resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmGlobalGhsMultiGenerator.cxx
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 "cmGlobalGhsMultiGenerator.h"
4
5 #include <algorithm>
6 #include <functional>
7 #include <map>
8 #include <sstream>
9 #include <utility>
10
11 #include <cm/memory>
12 #include <cm/string>
13 #include <cmext/algorithm>
14 #include <cmext/memory>
15
16 #include "cmCustomCommand.h"
17 #include "cmCustomCommandLines.h"
18 #include "cmDocumentationEntry.h"
19 #include "cmGeneratedFileStream.h"
20 #include "cmGeneratorTarget.h"
21 #include "cmGhsMultiGpj.h"
22 #include "cmLocalGenerator.h"
23 #include "cmLocalGhsMultiGenerator.h"
24 #include "cmMakefile.h"
25 #include "cmMessageType.h"
26 #include "cmPolicies.h"
27 #include "cmSourceFile.h"
28 #include "cmState.h"
29 #include "cmStateTypes.h"
30 #include "cmStringAlgorithms.h"
31 #include "cmSystemTools.h"
32 #include "cmTarget.h"
33 #include "cmValue.h"
34 #include "cmVersion.h"
35 #include "cmake.h"
36
37 const char* cmGlobalGhsMultiGenerator::FILE_EXTENSION = ".gpj";
38 #ifdef __linux__
39 const char* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild";
40 #elif defined(_WIN32)
41 const char* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild.exe";
42 #endif
43 const char* cmGlobalGhsMultiGenerator::CHECK_BUILD_SYSTEM_TARGET =
44   "RERUN_CMAKE";
45
46 cmGlobalGhsMultiGenerator::cmGlobalGhsMultiGenerator(cmake* cm)
47   : cmGlobalGenerator(cm)
48 {
49   cm->GetState()->SetGhsMultiIDE(true);
50 }
51
52 cmGlobalGhsMultiGenerator::~cmGlobalGhsMultiGenerator() = default;
53
54 std::unique_ptr<cmLocalGenerator>
55 cmGlobalGhsMultiGenerator::CreateLocalGenerator(cmMakefile* mf)
56 {
57   return std::unique_ptr<cmLocalGenerator>(
58     cm::make_unique<cmLocalGhsMultiGenerator>(this, mf));
59 }
60
61 void cmGlobalGhsMultiGenerator::GetDocumentation(cmDocumentationEntry& entry)
62 {
63   entry.Name = GetActualName();
64   entry.Brief =
65     "Generates Green Hills MULTI files (experimental, work-in-progress).";
66 }
67
68 void cmGlobalGhsMultiGenerator::ComputeTargetObjectDirectory(
69   cmGeneratorTarget* gt) const
70 {
71   // Compute full path to object file directory for this target.
72   std::string dir =
73     cmStrCat(gt->LocalGenerator->GetCurrentBinaryDirectory(), '/',
74              gt->LocalGenerator->GetTargetDirectory(gt), '/');
75   gt->ObjectDirectory = dir;
76 }
77
78 bool cmGlobalGhsMultiGenerator::SetGeneratorToolset(std::string const& ts,
79                                                     bool build, cmMakefile* mf)
80 {
81   /* In build mode nothing to be done.
82    * Toolset already determined and build tool absolute path is cached.
83    */
84   if (build) {
85     return true;
86   }
87
88   /* Determine the absolute directory for the toolset */
89   std::string tsp;
90   this->GetToolset(mf, tsp, ts);
91
92   /* no toolset was found */
93   if (tsp.empty()) {
94     return false;
95   }
96
97   /* set the build tool to use */
98   std::string gbuild(tsp + ((tsp.back() == '/') ? "" : "/") +
99                      DEFAULT_BUILD_PROGRAM);
100   cmValue prevTool = mf->GetDefinition("CMAKE_MAKE_PROGRAM");
101
102   /* check if the toolset changed from last generate */
103   if (cmNonempty(prevTool) && !cmSystemTools::ComparePath(gbuild, prevTool)) {
104     std::string const& e =
105       cmStrCat("toolset build tool: ", gbuild,
106                "\nDoes not match the previously used build tool: ", prevTool,
107                "\nEither remove the CMakeCache.txt file and CMakeFiles "
108                "directory or choose a different binary directory.");
109     mf->IssueMessage(MessageType::FATAL_ERROR, e);
110     return false;
111   }
112
113   /* store the toolset that is being used for this build */
114   mf->AddCacheDefinition("CMAKE_MAKE_PROGRAM", gbuild, "build program to use",
115                          cmStateEnums::INTERNAL, true);
116
117   mf->AddDefinition("CMAKE_SYSTEM_VERSION", tsp);
118
119   return true;
120 }
121
122 bool cmGlobalGhsMultiGenerator::SetGeneratorPlatform(std::string const& p,
123                                                      cmMakefile* mf)
124 {
125   /* set primary target */
126   cmValue t = mf->GetDefinition("GHS_PRIMARY_TARGET");
127   if (cmIsOff(t)) {
128     /* Use the value from `-A` or use `arm` */
129     std::string arch = "arm";
130     if (!cmIsOff(p)) {
131       arch = p;
132     }
133     cmValue platform = mf->GetDefinition("GHS_TARGET_PLATFORM");
134     std::string tgt = cmStrCat(arch, '_', platform, ".tgt");
135
136     /* update the primary target name*/
137     mf->AddDefinition("GHS_PRIMARY_TARGET", tgt);
138   }
139   return true;
140 }
141
142 void cmGlobalGhsMultiGenerator::EnableLanguage(
143   std::vector<std::string> const& l, cmMakefile* mf, bool optional)
144 {
145   mf->AddDefinition("CMAKE_SYSTEM_NAME", "GHS-MULTI");
146
147   mf->AddDefinition("GHSMULTI", "1"); // identifier for user CMake files
148
149   this->cmGlobalGenerator::EnableLanguage(l, mf, optional);
150 }
151
152 bool cmGlobalGhsMultiGenerator::FindMakeProgram(cmMakefile* /*mf*/)
153 {
154   // The GHS generator only knows how to lookup its build tool
155   // during generation of the project files, but this
156   // can only be done after the toolset is specified.
157
158   return true;
159 }
160
161 void cmGlobalGhsMultiGenerator::GetToolset(cmMakefile* mf, std::string& tsp,
162                                            const std::string& ts)
163 {
164   /* Determine tsp - full path of the toolset from ts (toolset hint via -T) */
165
166   std::string root = mf->GetSafeDefinition("GHS_TOOLSET_ROOT");
167
168   // Check if `-T` was set by user
169   if (ts.empty()) {
170     // Enter toolset search mode
171     std::vector<std::string> output;
172
173     // Make sure root exists...
174     if (!cmSystemTools::PathExists(root)) {
175       std::string msg =
176         "GHS_TOOLSET_ROOT directory \"" + root + "\" does not exist.";
177       mf->IssueMessage(MessageType::FATAL_ERROR, msg);
178       tsp = "";
179       return;
180     }
181
182     // Add a directory separator
183     if (root.back() != '/') {
184       root += "/";
185     }
186
187     // Get all compiler directories in toolset root
188     cmSystemTools::Glob(root, "comp_[^;]+", output);
189
190     if (output.empty()) {
191       // No compiler directories found
192       std::string msg =
193         "No GHS toolsets found in GHS_TOOLSET_ROOT \"" + root + "\".";
194       mf->IssueMessage(MessageType::FATAL_ERROR, msg);
195       tsp = "";
196     } else {
197       // Use latest? version
198       tsp = root + output.back();
199     }
200
201   } else {
202     // Toolset was provided by user
203     std::string tryPath;
204
205     // NOTE: CollapseFullPath() will determine if user toolset was full path or
206     //       or relative path.
207     tryPath = cmSystemTools::CollapseFullPath(ts, root);
208     if (!cmSystemTools::FileExists(tryPath)) {
209       std::string msg = "GHS toolset \"" + tryPath + "\" does not exist.";
210       mf->IssueMessage(MessageType::FATAL_ERROR, msg);
211       tsp = "";
212     } else {
213       tsp = tryPath;
214     }
215   }
216 }
217
218 void cmGlobalGhsMultiGenerator::WriteFileHeader(std::ostream& fout)
219 {
220   /* clang-format off */
221   fout << "#!gbuild\n"
222           "#\n"
223           "# CMAKE generated file: DO NOT EDIT!\n"
224           "# Generated by \"" << GetActualName() << "\""
225           " Generator, CMake Version " << cmVersion::GetMajorVersion() << '.'
226        << cmVersion::GetMinorVersion() << "\n"
227           "#\n\n";
228   /* clang-format on */
229 }
230
231 void cmGlobalGhsMultiGenerator::WriteCustomRuleBOD(std::ostream& fout)
232 {
233   fout << "Commands {\n"
234           "  Custom_Rule_Command {\n"
235           "    name = \"Custom Rule Command\"\n"
236           "    exec = \""
237 #ifdef _WIN32
238           "cmd.exe"
239 #else
240           "/bin/sh"
241 #endif
242           "\"\n"
243           "    options = {\"SpecialOptions\"}\n"
244           "  }\n"
245           "}\n"
246
247           "\n\n"
248           "FileTypes {\n"
249           "  CmakeRule {\n"
250           "    name = \"Custom Rule\"\n"
251           "    action = \"&Run\"\n"
252           "    extensions = {\""
253 #ifdef _WIN32
254           "bat"
255 #else
256           "sh"
257 #endif
258           "\"}\n"
259           "    grepable = false\n"
260           "    command = \"Custom Rule Command\"\n"
261           "    commandLine = \"$COMMAND "
262 #ifdef _WIN32
263           "/c"
264 #endif
265           " $INPUTFILE\"\n"
266           "    progress = \"Processing Custom Rule\"\n"
267           "    promoteToFirstPass = true\n"
268           "    outputType = \"None\"\n"
269           "    color = \"#800080\"\n"
270           "  }\n"
271           "}\n";
272 }
273
274 void cmGlobalGhsMultiGenerator::WriteCustomTargetBOD(std::ostream& fout)
275 {
276   fout << "FileTypes {\n"
277           "  CmakeTarget {\n"
278           "    name = \"Custom Target\"\n"
279           "    action = \"&Execute\"\n"
280           "    grepable = false\n"
281           "    outputType = \"None\"\n"
282           "    color = \"#800080\"\n"
283           "  }\n"
284           "}\n";
285 }
286
287 void cmGlobalGhsMultiGenerator::WriteTopLevelProject(std::ostream& fout,
288                                                      cmLocalGenerator* root)
289 {
290   this->WriteFileHeader(fout);
291   this->WriteMacros(fout, root);
292   this->WriteHighLevelDirectives(fout, root);
293   GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fout);
294
295   fout << "# Top Level Project File\n";
296
297   // Specify BSP option if supplied by user
298   // -- not all platforms require this entry in the project file
299   cmValue bspName = root->GetMakefile()->GetDefinition("GHS_BSP_NAME");
300   if (!cmIsOff(bspName)) {
301     fout << "    -bsp " << *bspName << '\n';
302   }
303
304   // Specify OS DIR if supplied by user
305   // -- not all platforms require this entry in the project file
306   cmValue osDir = root->GetMakefile()->GetDefinition("GHS_OS_DIR");
307   if (!cmIsOff(osDir)) {
308     cmValue osDirOption =
309       root->GetMakefile()->GetDefinition("GHS_OS_DIR_OPTION");
310     fout << "    ";
311     if (cmIsOff(osDirOption)) {
312       fout << "";
313     } else {
314       fout << *osDirOption;
315     }
316     fout << "\"" << osDir << "\"\n";
317   }
318 }
319
320 void cmGlobalGhsMultiGenerator::WriteSubProjects(std::ostream& fout,
321                                                  bool filterPredefined)
322 {
323   std::set<std::string> predefinedTargets;
324   predefinedTargets.insert(this->GetInstallTargetName());
325   predefinedTargets.insert(this->GetAllTargetName());
326   predefinedTargets.insert(std::string(CHECK_BUILD_SYSTEM_TARGET));
327
328   // All known targets
329   for (cmGeneratorTarget const* target : this->ProjectTargets) {
330     if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
331         target->GetType() == cmStateEnums::MODULE_LIBRARY ||
332         target->GetType() == cmStateEnums::SHARED_LIBRARY ||
333         (target->GetType() == cmStateEnums::GLOBAL_TARGET &&
334          target->GetName() != this->GetInstallTargetName())) {
335       continue;
336     }
337     /* Check if the current target is a predefined CMake target */
338     bool predefinedTarget =
339       predefinedTargets.find(target->GetName()) != predefinedTargets.end();
340     if ((filterPredefined && predefinedTarget) ||
341         (!filterPredefined && !predefinedTarget)) {
342       fout << target->GetName() + ".tgt" + FILE_EXTENSION << " [Project]\n";
343     }
344   }
345 }
346
347 void cmGlobalGhsMultiGenerator::WriteProjectLine(
348   std::ostream& fout, cmGeneratorTarget const* target,
349   std::string& rootBinaryDir)
350 {
351   cmValue projFile = target->GetProperty("GENERATOR_FILE_NAME");
352   cmValue projType = target->GetProperty("GENERATOR_FILE_NAME_EXT");
353   /* If either value is not valid then this particular target is an
354    * unsupported target type and should be skipped.
355    */
356   if (projFile && projType) {
357     std::string path = cmSystemTools::RelativePath(rootBinaryDir, projFile);
358
359     fout << path;
360     fout << ' ' << *projType << '\n';
361   }
362 }
363
364 void cmGlobalGhsMultiGenerator::WriteTargets(cmLocalGenerator* root)
365 {
366   std::string rootBinaryDir = root->GetCurrentBinaryDirectory();
367
368   // All known targets
369   for (cmGeneratorTarget const* target : this->ProjectTargets) {
370     if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
371         target->GetType() == cmStateEnums::MODULE_LIBRARY ||
372         target->GetType() == cmStateEnums::SHARED_LIBRARY ||
373         (target->GetType() == cmStateEnums::GLOBAL_TARGET &&
374          target->GetName() != this->GetInstallTargetName())) {
375       continue;
376     }
377
378     // create target build file
379     std::string name = cmStrCat(target->GetName(), ".tgt", FILE_EXTENSION);
380     std::string fname = cmStrCat(rootBinaryDir, "/", name);
381     cmGeneratedFileStream fbld(fname);
382     fbld.SetCopyIfDifferent(true);
383     this->WriteFileHeader(fbld);
384     GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fbld);
385     std::vector<cmGeneratorTarget const*> build;
386     if (this->ComputeTargetBuildOrder(target, build)) {
387       cmSystemTools::Error(
388         cmStrCat("The inter-target dependency graph for target [",
389                  target->GetName(), "] had a cycle.\n"));
390     } else {
391       for (auto& tgt : build) {
392         this->WriteProjectLine(fbld, tgt, rootBinaryDir);
393       }
394     }
395     fbld.Close();
396   }
397 }
398
399 void cmGlobalGhsMultiGenerator::Generate()
400 {
401   std::string fname;
402
403   // first do the superclass method
404   this->cmGlobalGenerator::Generate();
405
406   // output top-level projects
407   for (auto& it : this->ProjectMap) {
408     this->OutputTopLevelProject(it.second[0], it.second);
409   }
410
411   // create custom rule BOD file
412   fname = this->GetCMakeInstance()->GetHomeOutputDirectory() +
413     "/CMakeFiles/custom_rule.bod";
414   cmGeneratedFileStream frule(fname);
415   frule.SetCopyIfDifferent(true);
416   this->WriteFileHeader(frule);
417   this->WriteCustomRuleBOD(frule);
418   frule.Close();
419
420   // create custom target BOD file
421   fname = this->GetCMakeInstance()->GetHomeOutputDirectory() +
422     "/CMakeFiles/custom_target.bod";
423   cmGeneratedFileStream ftarget(fname);
424   ftarget.SetCopyIfDifferent(true);
425   this->WriteFileHeader(ftarget);
426   this->WriteCustomTargetBOD(ftarget);
427   ftarget.Close();
428 }
429
430 void cmGlobalGhsMultiGenerator::OutputTopLevelProject(
431   cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators)
432 {
433   std::string fname;
434
435   if (generators.empty()) {
436     return;
437   }
438
439   // Collect all targets under this root generator and the transitive
440   // closure of their dependencies.
441   TargetDependSet projectTargets;
442   TargetDependSet originalTargets;
443   this->GetTargetSets(projectTargets, originalTargets, root, generators);
444   OrderedTargetDependSet sortedProjectTargets(projectTargets, "");
445   this->ProjectTargets.clear();
446   for (cmGeneratorTarget const* t : sortedProjectTargets) {
447     /* save list of all targets in sorted order */
448     this->ProjectTargets.push_back(t);
449   }
450
451   /* Name top-level projects as filename.top.gpj to avoid name clashes
452    * with target projects.  This avoid the issue where the project has
453    * the same name as the executable target.
454    */
455   fname = cmStrCat(root->GetCurrentBinaryDirectory(), '/',
456                    root->GetProjectName(), ".top", FILE_EXTENSION);
457
458   cmGeneratedFileStream top(fname);
459   top.SetCopyIfDifferent(true);
460   this->WriteTopLevelProject(top, root);
461   this->WriteTargets(root);
462   this->WriteSubProjects(top, true);
463   this->WriteSubProjects(top, false);
464   top.Close();
465 }
466
467 std::vector<cmGlobalGenerator::GeneratedMakeCommand>
468 cmGlobalGhsMultiGenerator::GenerateBuildCommand(
469   const std::string& makeProgram, const std::string& projectName,
470   const std::string& projectDir, std::vector<std::string> const& targetNames,
471   const std::string& /*config*/, int jobs, bool verbose,
472   const cmBuildOptions& /*buildOptions*/,
473   std::vector<std::string> const& makeOptions)
474 {
475   GeneratedMakeCommand makeCommand;
476
477   makeCommand.Add(this->SelectMakeProgram(makeProgram));
478
479   if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) {
480     if (jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL) {
481       makeCommand.Add("-parallel");
482     } else {
483       makeCommand.Add(std::string("-parallel=") + std::to_string(jobs));
484     }
485   }
486
487   /* determine the top-project file in the project directory */
488   std::string proj = projectName + ".top" + FILE_EXTENSION;
489   std::vector<std::string> files;
490   cmSystemTools::Glob(projectDir, ".*\\.top\\.gpj", files);
491   if (!files.empty()) {
492     /* use the real top-level project in the directory */
493     proj = files.at(0);
494   }
495   makeCommand.Add("-top", proj);
496
497   /* determine targets to build */
498   bool build_all = false;
499   if (!targetNames.empty()) {
500     for (const auto& tname : targetNames) {
501       if (!tname.empty()) {
502         if (tname == "clean") {
503           makeCommand.Add("-clean");
504         } else {
505           makeCommand.Add(tname + ".tgt.gpj");
506         }
507       } else {
508         build_all = true;
509       }
510     }
511   } else {
512     build_all = true;
513   }
514
515   if (build_all) {
516     /* transform name to default build */;
517     std::string all = std::string(this->GetAllTargetName()) + ".tgt.gpj";
518     makeCommand.Add(all);
519   }
520
521   if (verbose) {
522     makeCommand.Add("-commands");
523   }
524   makeCommand.Add(makeOptions.begin(), makeOptions.end());
525
526   return { std::move(makeCommand) };
527 }
528
529 void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout,
530                                             cmLocalGenerator* root)
531 {
532   fout << "macro PROJ_NAME=" << root->GetProjectName() << '\n';
533   cmValue ghsGpjMacros = root->GetMakefile()->GetDefinition("GHS_GPJ_MACROS");
534   if (ghsGpjMacros) {
535     std::vector<std::string> expandedList = cmExpandedList(*ghsGpjMacros);
536     for (std::string const& arg : expandedList) {
537       fout << "macro " << arg << '\n';
538     }
539   }
540 }
541
542 void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives(
543   std::ostream& fout, cmLocalGenerator* root)
544 {
545   /* put primary target and customization files into project file */
546   cmValue const tgt = root->GetMakefile()->GetDefinition("GHS_PRIMARY_TARGET");
547
548   /* clang-format off */
549   fout << "primaryTarget=" << tgt << "\n"
550           "customization=" << root->GetBinaryDirectory()
551        << "/CMakeFiles/custom_rule.bod\n"
552           "customization=" << root->GetBinaryDirectory()
553        << "/CMakeFiles/custom_target.bod" << '\n';
554   /* clang-format on */
555
556   cmValue const customization =
557     root->GetMakefile()->GetDefinition("GHS_CUSTOMIZATION");
558   if (cmNonempty(customization)) {
559     fout << "customization="
560          << cmGlobalGhsMultiGenerator::TrimQuotes(*customization) << '\n';
561     this->GetCMakeInstance()->MarkCliAsUsed("GHS_CUSTOMIZATION");
562   }
563 }
564
565 std::string cmGlobalGhsMultiGenerator::TrimQuotes(std::string str)
566 {
567   cm::erase(str, '"');
568   return str;
569 }
570
571 bool cmGlobalGhsMultiGenerator::TargetCompare::operator()(
572   cmGeneratorTarget const* l, cmGeneratorTarget const* r) const
573 {
574   // Make sure a given named target is ordered first,
575   // e.g. to set ALL_BUILD as the default active project.
576   // When the empty string is named this is a no-op.
577   if (r->GetName() == this->First) {
578     return false;
579   }
580   if (l->GetName() == this->First) {
581     return true;
582   }
583   return l->GetName() < r->GetName();
584 }
585
586 cmGlobalGhsMultiGenerator::OrderedTargetDependSet::OrderedTargetDependSet(
587   TargetDependSet const& targets, std::string const& first)
588   : derived(TargetCompare(first))
589 {
590   this->insert(targets.begin(), targets.end());
591 }
592
593 bool cmGlobalGhsMultiGenerator::ComputeTargetBuildOrder(
594   cmGeneratorTarget const* tgt, std::vector<cmGeneratorTarget const*>& build)
595 {
596   std::vector<cmGeneratorTarget const*> t{ tgt };
597   return this->ComputeTargetBuildOrder(t, build);
598 }
599
600 bool cmGlobalGhsMultiGenerator::ComputeTargetBuildOrder(
601   std::vector<cmGeneratorTarget const*>& tgt,
602   std::vector<cmGeneratorTarget const*>& build)
603 {
604   std::set<cmGeneratorTarget const*> temp;
605   std::set<cmGeneratorTarget const*> perm;
606
607   for (const auto* const ti : tgt) {
608     bool r = this->VisitTarget(temp, perm, build, ti);
609     if (r) {
610       return r;
611     }
612   }
613   return false;
614 }
615
616 bool cmGlobalGhsMultiGenerator::VisitTarget(
617   std::set<cmGeneratorTarget const*>& temp,
618   std::set<cmGeneratorTarget const*>& perm,
619   std::vector<cmGeneratorTarget const*>& order, cmGeneratorTarget const* ti)
620 {
621   /* check if permanent mark is set*/
622   if (perm.find(ti) == perm.end()) {
623     /* set temporary mark; check if revisit*/
624     if (temp.insert(ti).second) {
625       /* sort targets lexicographically to ensure that nodes are always visited
626        * in the same order */
627       OrderedTargetDependSet sortedTargets(this->GetTargetDirectDepends(ti),
628                                            "");
629       for (auto const& di : sortedTargets) {
630         if (this->VisitTarget(temp, perm, order, di)) {
631           return true;
632         }
633       }
634       /* mark as complete; insert into beginning of list*/
635       perm.insert(ti);
636       order.push_back(ti);
637       return false;
638     }
639     /* revisiting item - not a DAG */
640     return true;
641   }
642   /* already complete */
643   return false;
644 }
645
646 bool cmGlobalGhsMultiGenerator::AddCheckTarget()
647 {
648   // Skip the target if no regeneration is to be done.
649   if (this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) {
650     return false;
651   }
652
653   // Get the generators.
654   std::vector<std::unique_ptr<cmLocalGenerator>> const& generators =
655     this->LocalGenerators;
656   auto& lg =
657     cm::static_reference_cast<cmLocalGhsMultiGenerator>(generators[0]);
658
659   // The name of the output file for the custom command.
660   this->StampFile = lg.GetBinaryDirectory() + std::string("/CMakeFiles/") +
661     CHECK_BUILD_SYSTEM_TARGET;
662
663   // Add a custom rule to re-run CMake if any input files changed.
664   {
665     // Collect the input files used to generate all targets in this
666     // project.
667     std::vector<std::string> listFiles;
668     for (const auto& gen : generators) {
669       cm::append(listFiles, gen->GetMakefile()->GetListFiles());
670     }
671
672     // Add the cache file.
673     listFiles.push_back(cmStrCat(
674       this->GetCMakeInstance()->GetHomeOutputDirectory(), "/CMakeCache.txt"));
675
676     // Print not implemented warning.
677     if (this->GetCMakeInstance()->DoWriteGlobVerifyTarget()) {
678       std::ostringstream msg;
679       msg << "Any pre-check scripts, such as those generated for file(GLOB "
680              "CONFIGURE_DEPENDS), will not be run by gbuild.";
681       this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING,
682                                              msg.str());
683     }
684
685     // Sort the list of input files and remove duplicates.
686     std::sort(listFiles.begin(), listFiles.end(), std::less<std::string>());
687     auto newEnd = std::unique(listFiles.begin(), listFiles.end());
688     listFiles.erase(newEnd, listFiles.end());
689
690     // Create a rule to re-run CMake and create output file.
691     cmCustomCommandLines commandLines;
692     commandLines.emplace_back(
693       cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", "rm", "-f",
694                           this->StampFile }));
695     std::string argS = cmStrCat("-S", lg.GetSourceDirectory());
696     std::string argB = cmStrCat("-B", lg.GetBinaryDirectory());
697     commandLines.emplace_back(
698       cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), argS, argB }));
699     commandLines.emplace_back(cmMakeCommandLine(
700       { cmSystemTools::GetCMakeCommand(), "-E", "touch", this->StampFile }));
701
702     /* Create the target(Exclude from ALL_BUILD).
703      *
704      * The build tool, currently, does not support rereading the project files
705      * if they get updated. So do not run this target as part of ALL_BUILD.
706      */
707     auto cc = cm::make_unique<cmCustomCommand>();
708     cmTarget* tgt =
709       lg.AddUtilityCommand(CHECK_BUILD_SYSTEM_TARGET, true, std::move(cc));
710     auto ptr = cm::make_unique<cmGeneratorTarget>(tgt, &lg);
711     auto* gt = ptr.get();
712     lg.AddGeneratorTarget(std::move(ptr));
713
714     // Add the rule.
715     cc = cm::make_unique<cmCustomCommand>();
716     cc->SetOutputs(this->StampFile);
717     cc->SetDepends(listFiles);
718     cc->SetCommandLines(commandLines);
719     cc->SetComment("Checking Build System");
720     cc->SetCMP0116Status(cmPolicies::NEW);
721     cc->SetEscapeOldStyle(false);
722     cc->SetStdPipesUTF8(true);
723
724     if (cmSourceFile* file =
725           lg.AddCustomCommandToOutput(std::move(cc), true)) {
726       gt->AddSource(file->ResolveFullPath());
727     } else {
728       cmSystemTools::Error("Error adding rule for " + this->StampFile);
729     }
730     // Organize in the "predefined targets" folder:
731     if (this->UseFolderProperty()) {
732       tgt->SetProperty("FOLDER", this->GetPredefinedTargetsFolder());
733     }
734   }
735
736   return true;
737 }
738
739 void cmGlobalGhsMultiGenerator::AddAllTarget()
740 {
741   // Add a special target that depends on ALL projects for easy build
742   // of one configuration only.
743   for (auto const& it : this->ProjectMap) {
744     std::vector<cmLocalGenerator*> const& gen = it.second;
745     // add the ALL_BUILD to the first local generator of each project
746     if (!gen.empty()) {
747       // Use no actual command lines so that the target itself is not
748       // considered always out of date.
749       auto cc = cm::make_unique<cmCustomCommand>();
750       cc->SetCMP0116Status(cmPolicies::NEW);
751       cc->SetEscapeOldStyle(false);
752       cc->SetComment("Build all projects");
753       cmTarget* allBuild = gen[0]->AddUtilityCommand(this->GetAllTargetName(),
754                                                      true, std::move(cc));
755
756       gen[0]->AddGeneratorTarget(
757         cm::make_unique<cmGeneratorTarget>(allBuild, gen[0]));
758
759       // Organize in the "predefined targets" folder:
760       if (this->UseFolderProperty()) {
761         allBuild->SetProperty("FOLDER", this->GetPredefinedTargetsFolder());
762       }
763
764       // Now make all targets depend on the ALL_BUILD target
765       for (cmLocalGenerator const* i : gen) {
766         for (const auto& tgt : i->GetGeneratorTargets()) {
767           // Skip global or imported targets
768           if (tgt->GetType() == cmStateEnums::GLOBAL_TARGET ||
769               tgt->IsImported()) {
770             continue;
771           }
772           // Skip Exclude From All Targets
773           if (!this->IsExcluded(gen[0], tgt.get())) {
774             allBuild->AddUtility(tgt->GetName(), false);
775           }
776         }
777       }
778     }
779   }
780 }
781
782 void cmGlobalGhsMultiGenerator::AddExtraIDETargets()
783 {
784   // Add a special target that depends on ALL projects.
785   this->AddAllTarget();
786
787   /* Add Custom Target to check if CMake needs to be rerun.
788    *
789    * The build tool, currently, does not support rereading the project files
790    * if they get updated.  So do not make the other targets dependent on this
791    * check.
792    */
793   this->AddCheckTarget();
794 }