bf019c35e17d969932d97944a970fb4fea0a6dad
[platform/upstream/cmake.git] / Source / cmGhsMultiTargetGenerator.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 "cmGhsMultiTargetGenerator.h"
4
5 #include <algorithm>
6 #include <memory>
7 #include <ostream>
8 #include <set>
9 #include <utility>
10 #include <vector>
11
12 #include "cmCustomCommand.h"
13 #include "cmCustomCommandGenerator.h"
14 #include "cmGeneratedFileStream.h"
15 #include "cmGeneratorTarget.h"
16 #include "cmGlobalGhsMultiGenerator.h"
17 #include "cmLinkLineComputer.h" // IWYU pragma: keep
18 #include "cmLocalGenerator.h"
19 #include "cmLocalGhsMultiGenerator.h"
20 #include "cmMakefile.h"
21 #include "cmOutputConverter.h"
22 #include "cmSourceFile.h"
23 #include "cmSourceFileLocation.h"
24 #include "cmSourceGroup.h"
25 #include "cmStateDirectory.h"
26 #include "cmStateSnapshot.h"
27 #include "cmStateTypes.h"
28 #include "cmStringAlgorithms.h"
29 #include "cmSystemTools.h"
30 #include "cmTarget.h"
31 #include "cmValue.h"
32
33 cmGhsMultiTargetGenerator::cmGhsMultiTargetGenerator(cmGeneratorTarget* target)
34   : GeneratorTarget(target)
35   , LocalGenerator(
36       static_cast<cmLocalGhsMultiGenerator*>(target->GetLocalGenerator()))
37   , Makefile(target->Target->GetMakefile())
38   , Name(target->GetName())
39 {
40   // Store the configuration name that is being used
41   if (cmValue config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE")) {
42     // Use the build type given by the user.
43     this->ConfigName = *config;
44   } else {
45     // No configuration type given.
46     this->ConfigName.clear();
47   }
48 }
49
50 cmGhsMultiTargetGenerator::~cmGhsMultiTargetGenerator() = default;
51
52 void cmGhsMultiTargetGenerator::Generate()
53 {
54   // Determine type of target for this project
55   switch (this->GeneratorTarget->GetType()) {
56     case cmStateEnums::EXECUTABLE: {
57       // Get the name of the executable to generate.
58       this->TargetNameReal =
59         this->GeneratorTarget->GetExecutableNames(this->ConfigName).Real;
60       if (this->cmGhsMultiTargetGenerator::DetermineIfIntegrityApp()) {
61         this->TagType = GhsMultiGpj::INTERGRITY_APPLICATION;
62       } else {
63         this->TagType = GhsMultiGpj::PROGRAM;
64       }
65       break;
66     }
67     case cmStateEnums::STATIC_LIBRARY: {
68       this->TargetNameReal =
69         this->GeneratorTarget->GetLibraryNames(this->ConfigName).Real;
70       this->TagType = GhsMultiGpj::LIBRARY;
71       break;
72     }
73     case cmStateEnums::SHARED_LIBRARY: {
74       std::string msg =
75         cmStrCat("add_library(<name> SHARED ...) not supported: ", this->Name);
76       cmSystemTools::Message(msg);
77       return;
78     }
79     case cmStateEnums::OBJECT_LIBRARY: {
80       this->TargetNameReal =
81         this->GeneratorTarget->GetLibraryNames(this->ConfigName).Real;
82       this->TagType = GhsMultiGpj::SUBPROJECT;
83       break;
84     }
85     case cmStateEnums::MODULE_LIBRARY: {
86       std::string msg =
87         cmStrCat("add_library(<name> MODULE ...) not supported: ", this->Name);
88       cmSystemTools::Message(msg);
89       return;
90     }
91     case cmStateEnums::UTILITY: {
92       this->TargetNameReal = this->GeneratorTarget->GetName();
93       this->TagType = GhsMultiGpj::CUSTOM_TARGET;
94       break;
95     }
96     case cmStateEnums::GLOBAL_TARGET: {
97       this->TargetNameReal = this->GeneratorTarget->GetName();
98       if (this->TargetNameReal ==
99           this->GetGlobalGenerator()->GetInstallTargetName()) {
100         this->TagType = GhsMultiGpj::CUSTOM_TARGET;
101       } else {
102         return;
103       }
104       break;
105     }
106     default:
107       return;
108   }
109
110   this->GenerateTarget();
111 }
112
113 void cmGhsMultiTargetGenerator::GenerateTarget()
114 {
115   // Open the target file in copy-if-different mode.
116   std::string fproj =
117     cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
118              this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
119              '/', this->Name, cmGlobalGhsMultiGenerator::FILE_EXTENSION);
120
121   // Tell the global generator the name of the project file
122   this->GeneratorTarget->Target->SetProperty("GENERATOR_FILE_NAME", fproj);
123   this->GeneratorTarget->Target->SetProperty(
124     "GENERATOR_FILE_NAME_EXT", GhsMultiGpj::GetGpjTag(this->TagType));
125
126   cmGeneratedFileStream fout(fproj);
127   fout.SetCopyIfDifferent(true);
128
129   this->GetGlobalGenerator()->WriteFileHeader(fout);
130   GhsMultiGpj::WriteGpjTag(this->TagType, fout);
131
132   if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) {
133     const std::string language(
134       this->GeneratorTarget->GetLinkerLanguage(this->ConfigName));
135     this->WriteTargetSpecifics(fout, this->ConfigName);
136     this->SetCompilerFlags(this->ConfigName, language);
137     this->WriteCompilerFlags(fout, this->ConfigName, language);
138     this->WriteCompilerDefinitions(fout, this->ConfigName, language);
139     this->WriteIncludes(fout, this->ConfigName, language);
140     this->WriteTargetLinkLine(fout, this->ConfigName);
141     this->WriteBuildEvents(fout);
142   }
143   this->WriteSources(fout);
144   fout.Close();
145 }
146
147 cmGlobalGhsMultiGenerator* cmGhsMultiTargetGenerator::GetGlobalGenerator()
148   const
149 {
150   return static_cast<cmGlobalGhsMultiGenerator*>(
151     this->LocalGenerator->GetGlobalGenerator());
152 }
153
154 void cmGhsMultiTargetGenerator::WriteTargetSpecifics(std::ostream& fout,
155                                                      const std::string& config)
156 {
157   std::string outpath;
158
159   /* Determine paths from the target project file to where the output artifacts
160    * need to be located.
161    */
162   if (this->TagType != GhsMultiGpj::SUBPROJECT) {
163     // set target binary file destination
164     std::string binpath = cmStrCat(
165       this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
166       this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget));
167     outpath = cmSystemTools::RelativePath(
168       binpath, this->GeneratorTarget->GetDirectory(config));
169     /* clang-format off */
170     fout << "    :binDirRelative=\"" << outpath << "\"\n"
171             "    -o \"" << this->TargetNameReal << "\"\n";
172     /* clang-format on */
173   }
174
175   // set target object file destination
176   outpath = ".";
177   fout << "    :outputDirRelative=\"" << outpath << "\"\n";
178 }
179
180 void cmGhsMultiTargetGenerator::SetCompilerFlags(std::string const& config,
181                                                  const std::string& language)
182 {
183   auto i = this->FlagsByLanguage.find(language);
184   if (i == this->FlagsByLanguage.end()) {
185     std::string flags;
186     this->LocalGenerator->AddLanguageFlags(flags, this->GeneratorTarget,
187                                            language, config);
188     this->LocalGenerator->AddCMP0018Flags(flags, this->GeneratorTarget,
189                                           language, config);
190     this->LocalGenerator->AddVisibilityPresetFlags(
191       flags, this->GeneratorTarget, language);
192     this->LocalGenerator->AddColorDiagnosticsFlags(flags, language);
193
194     // Append old-style preprocessor definition flags.
195     if (this->Makefile->GetDefineFlags() != " ") {
196       this->LocalGenerator->AppendFlags(flags,
197                                         this->Makefile->GetDefineFlags());
198     }
199
200     // Add target-specific flags.
201     this->LocalGenerator->AddCompileOptions(flags, this->GeneratorTarget,
202                                             language, config);
203
204     std::map<std::string, std::string>::value_type entry(language, flags);
205     i = this->FlagsByLanguage.insert(entry).first;
206   }
207 }
208
209 std::string cmGhsMultiTargetGenerator::GetDefines(const std::string& language,
210                                                   std::string const& config)
211 {
212   auto i = this->DefinesByLanguage.find(language);
213   if (i == this->DefinesByLanguage.end()) {
214     std::set<std::string> defines;
215     // Add preprocessor definitions for this target and configuration.
216     this->LocalGenerator->GetTargetDefines(this->GeneratorTarget, config,
217                                            language, defines);
218
219     std::string definesString;
220     this->LocalGenerator->JoinDefines(defines, definesString, language);
221
222     std::map<std::string, std::string>::value_type entry(language,
223                                                          definesString);
224     i = this->DefinesByLanguage.insert(entry).first;
225   }
226   return i->second;
227 }
228
229 void cmGhsMultiTargetGenerator::WriteCompilerFlags(std::ostream& fout,
230                                                    std::string const&,
231                                                    const std::string& language)
232 {
233   auto flagsByLangI = this->FlagsByLanguage.find(language);
234   if (flagsByLangI != this->FlagsByLanguage.end()) {
235     if (!flagsByLangI->second.empty()) {
236       std::vector<std::string> ghsCompFlags =
237         cmSystemTools::ParseArguments(flagsByLangI->second);
238       for (const std::string& f : ghsCompFlags) {
239         fout << "    " << f << '\n';
240       }
241     }
242   }
243 }
244
245 void cmGhsMultiTargetGenerator::WriteCompilerDefinitions(
246   std::ostream& fout, const std::string& config, const std::string& language)
247 {
248   std::vector<std::string> compileDefinitions;
249   this->GeneratorTarget->GetCompileDefinitions(compileDefinitions, config,
250                                                language);
251   for (std::string const& compileDefinition : compileDefinitions) {
252     fout << "    -D" << compileDefinition << '\n';
253   }
254 }
255
256 void cmGhsMultiTargetGenerator::WriteIncludes(std::ostream& fout,
257                                               const std::string& config,
258                                               const std::string& language)
259 {
260   std::vector<std::string> includes;
261   this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget,
262                                               language, config);
263
264   for (std::string const& include : includes) {
265     fout << "    -I\"" << include << "\"\n";
266   }
267 }
268
269 void cmGhsMultiTargetGenerator::WriteTargetLinkLine(std::ostream& fout,
270                                                     std::string const& config)
271 {
272   if (this->TagType == GhsMultiGpj::INTERGRITY_APPLICATION) {
273     return;
274   }
275
276   std::string linkLibraries;
277   std::string flags;
278   std::string linkFlags;
279   std::string frameworkPath;
280   std::string linkPath;
281
282   std::unique_ptr<cmLinkLineComputer> linkLineComputer =
283     this->GetGlobalGenerator()->CreateLinkLineComputer(
284       this->LocalGenerator,
285       this->LocalGenerator->GetStateSnapshot().GetDirectory());
286
287   this->LocalGenerator->GetTargetFlags(
288     linkLineComputer.get(), config, linkLibraries, flags, linkFlags,
289     frameworkPath, linkPath, this->GeneratorTarget);
290
291   // write out link options
292   std::vector<std::string> lopts = cmSystemTools::ParseArguments(linkFlags);
293   for (const std::string& l : lopts) {
294     fout << "    " << l << '\n';
295   }
296
297   // write out link search paths
298   // must be quoted for paths that contain spaces
299   std::vector<std::string> lpath = cmSystemTools::ParseArguments(linkPath);
300   for (const std::string& l : lpath) {
301     fout << "    -L\"" << l << "\"\n";
302   }
303
304   // write out link libs
305   // must be quoted for filepaths that contains spaces
306   std::string cbd = this->LocalGenerator->GetCurrentBinaryDirectory();
307
308   std::vector<std::string> llibs =
309     cmSystemTools::ParseArguments(linkLibraries);
310   for (const std::string& l : llibs) {
311     if (l.compare(0, 2, "-l") == 0) {
312       fout << "    \"" << l << "\"\n";
313     } else {
314       std::string rl = cmSystemTools::CollapseFullPath(l, cbd);
315       fout << "    -l\"" << rl << "\"\n";
316     }
317   }
318 }
319
320 void cmGhsMultiTargetGenerator::WriteBuildEvents(std::ostream& fout)
321 {
322   this->WriteBuildEventsHelper(fout,
323                                this->GeneratorTarget->GetPreBuildCommands(),
324                                std::string("prebuild"),
325 #ifdef _WIN32
326                                std::string("preexecShell")
327 #else
328                                std::string("preexec")
329 #endif
330   );
331
332   if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) {
333     this->WriteBuildEventsHelper(fout,
334                                  this->GeneratorTarget->GetPreLinkCommands(),
335                                  std::string("prelink"),
336 #ifdef _WIN32
337                                  std::string("preexecShell")
338 #else
339                                  std::string("preexec")
340 #endif
341     );
342   }
343
344   this->WriteBuildEventsHelper(fout,
345                                this->GeneratorTarget->GetPostBuildCommands(),
346                                std::string("postbuild"),
347 #ifdef _WIN32
348                                std::string("postexecShell")
349 #else
350                                std::string("postexec")
351 #endif
352   );
353 }
354
355 void cmGhsMultiTargetGenerator::WriteBuildEventsHelper(
356   std::ostream& fout, const std::vector<cmCustomCommand>& ccv,
357   std::string const& name, std::string const& cmd)
358 {
359   int cmdcount = 0;
360 #ifdef _WIN32
361   std::string fext = ".bat";
362   std::string shell;
363 #else
364   std::string fext = ".sh";
365   std::string shell = "/bin/sh ";
366 #endif
367
368   for (cmCustomCommand const& cc : ccv) {
369     cmCustomCommandGenerator ccg(cc, this->ConfigName, this->LocalGenerator);
370     // Open the filestream for this custom command
371     std::string fname =
372       cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
373                this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
374                '/', this->Name, '_', name, cmdcount++, fext);
375
376     cmGeneratedFileStream f(fname);
377     f.SetCopyIfDifferent(true);
378     this->WriteCustomCommandsHelper(f, ccg);
379     f.Close();
380     if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) {
381       fout << "    :" << cmd << "=\"" << shell << fname << "\"\n";
382     } else {
383       fout << fname << "\n    :outputName=\"" << fname << ".rule\"\n";
384     }
385     for (const auto& byp : ccg.GetByproducts()) {
386       fout << "    :extraOutputFile=\"" << byp << "\"\n";
387     }
388   }
389 }
390
391 void cmGhsMultiTargetGenerator::WriteCustomCommandsHelper(
392   std::ostream& fout, cmCustomCommandGenerator const& ccg)
393 {
394   std::vector<std::string> cmdLines;
395
396   // if the command specified a working directory use it.
397   std::string dir = this->LocalGenerator->GetCurrentBinaryDirectory();
398   std::string workingDir = ccg.GetWorkingDirectory();
399   if (!workingDir.empty()) {
400     dir = workingDir;
401   }
402
403   // Line to check for error between commands.
404 #ifdef _WIN32
405   std::string check_error = "if %errorlevel% neq 0 exit /b %errorlevel%";
406 #else
407   std::string check_error = "if [ $? -ne 0 ]; then exit 1; fi";
408 #endif
409
410 #ifdef _WIN32
411   cmdLines.push_back("@echo off");
412 #endif
413   // Echo the custom command's comment text.
414   const char* comment = ccg.GetComment();
415   if (comment && *comment) {
416     std::string echocmd = cmStrCat("echo ", comment);
417     cmdLines.push_back(std::move(echocmd));
418   }
419
420   // Switch to working directory
421   std::string cdCmd;
422 #ifdef _WIN32
423   std::string cdStr = "cd /D ";
424 #else
425   std::string cdStr = "cd ";
426 #endif
427   cdCmd = cdStr +
428     this->LocalGenerator->ConvertToOutputFormat(dir, cmOutputConverter::SHELL);
429   cmdLines.push_back(std::move(cdCmd));
430
431   for (unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) {
432     // Build the command line in a single string.
433     std::string cmd = ccg.GetCommand(c);
434     if (!cmd.empty()) {
435       // Use "call " before any invocations of .bat or .cmd files
436       // invoked as custom commands in the WindowsShell.
437       //
438       bool useCall = false;
439
440 #ifdef _WIN32
441       std::string suffix;
442       if (cmd.size() > 4) {
443         suffix = cmSystemTools::LowerCase(cmd.substr(cmd.size() - 4));
444         if (suffix == ".bat" || suffix == ".cmd") {
445           useCall = true;
446         }
447       }
448 #endif
449
450       cmSystemTools::ReplaceString(cmd, "/./", "/");
451       // Convert the command to a relative path only if the current
452       // working directory will be the start-output directory.
453       bool had_slash = cmd.find('/') != std::string::npos;
454       if (workingDir.empty()) {
455         cmd = this->LocalGenerator->MaybeRelativeToCurBinDir(cmd);
456       }
457       bool has_slash = cmd.find('/') != std::string::npos;
458       if (had_slash && !has_slash) {
459         // This command was specified as a path to a file in the
460         // current directory.  Add a leading "./" so it can run
461         // without the current directory being in the search path.
462         cmd = cmStrCat("./", cmd);
463       }
464       cmd = this->LocalGenerator->ConvertToOutputFormat(
465         cmd, cmOutputConverter::SHELL);
466       if (useCall) {
467         cmd = cmStrCat("call ", cmd);
468       }
469       ccg.AppendArguments(c, cmd);
470       cmdLines.push_back(std::move(cmd));
471     }
472   }
473
474   // push back the custom commands
475   for (auto const& c : cmdLines) {
476     fout << c << '\n' << check_error << '\n';
477   }
478 }
479
480 void cmGhsMultiTargetGenerator::WriteSourceProperty(
481   std::ostream& fout, const cmSourceFile* sf, std::string const& propName,
482   std::string const& propFlag)
483 {
484   cmValue prop = sf->GetProperty(propName);
485   if (prop) {
486     std::vector<std::string> list = cmExpandedList(*prop);
487     for (const std::string& p : list) {
488       fout << "    " << propFlag << p << '\n';
489     }
490   }
491 }
492
493 void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj)
494 {
495   /* vector of all sources for this target */
496   std::vector<cmSourceFile*> sources;
497   this->GeneratorTarget->GetSourceFiles(sources, this->ConfigName);
498
499   /* vector of all groups defined for this target
500    * -- but the vector is not expanded with sub groups or in any useful order
501    */
502   std::vector<cmSourceGroup> sourceGroups = this->Makefile->GetSourceGroups();
503
504   /* for each source file assign it to its group */
505   std::map<std::string, std::vector<cmSourceFile*>> groupFiles;
506   std::set<std::string> groupNames;
507   for (cmSourceFile* sf : sources) {
508     cmSourceGroup* sourceGroup =
509       this->Makefile->FindSourceGroup(sf->ResolveFullPath(), sourceGroups);
510     std::string gn = sourceGroup->GetFullName();
511     groupFiles[gn].push_back(sf);
512     groupNames.insert(std::move(gn));
513   }
514
515   /* list of known groups and the order they are displayed in a project file */
516   const std::vector<std::string> standardGroups = {
517     "CMake Rules",  "Header Files",     "Source Files",
518     "Object Files", "Object Libraries", "Resources"
519   };
520
521   /* list of groups in the order they are displayed in a project file*/
522   std::vector<std::string> groupFilesList(groupFiles.size());
523
524   /* put the groups in the order they should be listed
525    * - standard groups first, and then everything else
526    *   in the order used by std::map.
527    */
528   int i = 0;
529   for (const std::string& gn : standardGroups) {
530     auto n = groupNames.find(gn);
531     if (n != groupNames.end()) {
532       groupFilesList[i] = *n;
533       i += 1;
534       groupNames.erase(gn);
535     } else if (this->TagType == GhsMultiGpj::CUSTOM_TARGET &&
536                gn == "CMake Rules") {
537       /* make sure that rules folder always exists in case of custom targets
538        * that have no custom commands except for pre or post build events.
539        */
540       groupFilesList.resize(groupFilesList.size() + 1);
541       groupFilesList[i] = gn;
542       i += 1;
543     }
544   }
545
546   { /* catch-all group - is last item */
547     std::string gn;
548     auto n = groupNames.find(gn);
549     if (n != groupNames.end()) {
550       groupFilesList.back() = *n;
551       groupNames.erase(gn);
552     }
553   }
554
555   for (const auto& n : groupNames) {
556     groupFilesList[i] = n;
557     i += 1;
558   }
559
560   /* sort the files within each group */
561   for (auto& n : groupFilesList) {
562     std::sort(groupFiles[n].begin(), groupFiles[n].end(),
563               [](cmSourceFile* l, cmSourceFile* r) {
564                 return l->ResolveFullPath() < r->ResolveFullPath();
565               });
566   }
567
568   /* list of open project files */
569   std::vector<cmGeneratedFileStream*> gfiles;
570
571   /* write files into the proper project file
572    * -- groups go into main project file
573    *    unless NO_SOURCE_GROUP_FILE property or variable is set.
574    */
575   for (auto& sg : groupFilesList) {
576     std::ostream* fout;
577     bool useProjectFile =
578       cmIsOn(this->GeneratorTarget->GetProperty("GHS_NO_SOURCE_GROUP_FILE")) ||
579       this->Makefile->IsOn("CMAKE_GHS_NO_SOURCE_GROUP_FILE");
580     if (useProjectFile || sg.empty()) {
581       fout = &fout_proj;
582     } else {
583       // Open the filestream in copy-if-different mode.
584       std::string gname = sg;
585       cmsys::SystemTools::ReplaceString(gname, "\\", "_");
586       std::string lpath =
587         cmStrCat(gname, cmGlobalGhsMultiGenerator::FILE_EXTENSION);
588       std::string fpath = cmStrCat(
589         this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
590         this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget), '/',
591         lpath);
592       cmGeneratedFileStream* f = new cmGeneratedFileStream(fpath);
593       f->SetCopyIfDifferent(true);
594       gfiles.push_back(f);
595       fout = f;
596       this->GetGlobalGenerator()->WriteFileHeader(*f);
597       GhsMultiGpj::WriteGpjTag(GhsMultiGpj::SUBPROJECT, *f);
598       fout_proj << lpath << " ";
599       GhsMultiGpj::WriteGpjTag(GhsMultiGpj::SUBPROJECT, fout_proj);
600     }
601
602     if (useProjectFile) {
603       if (sg.empty()) {
604         *fout << "{comment} Others" << '\n';
605       } else {
606         *fout << "{comment} " << sg << '\n';
607       }
608     } else if (sg.empty()) {
609       *fout << "{comment} Others\n";
610     }
611
612     if (sg != "CMake Rules") {
613       /* output rule for each source file */
614       for (const cmSourceFile* si : groupFiles[sg]) {
615         bool compile = true;
616         // Convert filename to native system
617         // WORKAROUND: GHS MULTI 6.1.4 and 6.1.6 are known to need backslash on
618         // windows when opening some files from the search window.
619         std::string fname(si->GetFullPath());
620         cmSystemTools::ConvertToOutputSlashes(fname);
621
622         /* For custom targets list any associated sources,
623          * comment out source code to prevent it from being
624          * compiled when processing this target.
625          * Otherwise, comment out any custom command (main) dependencies that
626          * are listed as source files to prevent them from being considered
627          * part of the build.
628          */
629         std::string comment;
630         if ((this->TagType == GhsMultiGpj::CUSTOM_TARGET &&
631              !si->GetLanguage().empty()) ||
632             si->GetCustomCommand()) {
633           comment = "{comment} ";
634           compile = false;
635         }
636
637         *fout << comment << fname << WriteObjectLangOverride(si) << '\n';
638         if (compile) {
639           this->WriteSourceProperty(*fout, si, "INCLUDE_DIRECTORIES", "-I");
640           this->WriteSourceProperty(*fout, si, "COMPILE_DEFINITIONS", "-D");
641           this->WriteSourceProperty(*fout, si, "COMPILE_OPTIONS", "");
642
643           /* to avoid clutter in the GUI only print out the objectName if it
644            * has been renamed */
645           std::string objectName = this->GeneratorTarget->GetObjectName(si);
646           if (!objectName.empty() &&
647               this->GeneratorTarget->HasExplicitObjectName(si)) {
648             *fout << "    -o " << objectName << '\n';
649           }
650         }
651       }
652     } else {
653       std::vector<cmSourceFile const*> customCommands;
654       if (this->ComputeCustomCommandOrder(customCommands)) {
655         std::string message = "The custom commands for target [" +
656           this->GeneratorTarget->GetName() + "] had a cycle.\n";
657         cmSystemTools::Error(message);
658       } else {
659         /* Custom targets do not have a dependency on SOURCES files.
660          * Therefore the dependency list may include SOURCES files after the
661          * custom target. Because nothing can depend on the custom target just
662          * move it to the last item.
663          */
664         for (auto sf = customCommands.begin(); sf != customCommands.end();
665              ++sf) {
666           if (((*sf)->GetLocation()).GetName() == this->Name + ".rule") {
667             std::rotate(sf, sf + 1, customCommands.end());
668             break;
669           }
670         }
671         int cmdcount = 0;
672 #ifdef _WIN32
673         std::string fext = ".bat";
674 #else
675         std::string fext = ".sh";
676 #endif
677         for (auto& sf : customCommands) {
678           const cmCustomCommand* cc = sf->GetCustomCommand();
679           cmCustomCommandGenerator ccg(*cc, this->ConfigName,
680                                        this->LocalGenerator);
681
682           // Open the filestream for this custom command
683           std::string fname = cmStrCat(
684             this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
685             this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
686             '/', this->Name, "_cc", cmdcount++, '_',
687             (sf->GetLocation()).GetName(), fext);
688
689           cmGeneratedFileStream f(fname);
690           f.SetCopyIfDifferent(true);
691           this->WriteCustomCommandsHelper(f, ccg);
692           f.Close();
693           this->WriteCustomCommandLine(*fout, fname, ccg);
694         }
695       }
696       if (this->TagType == GhsMultiGpj::CUSTOM_TARGET) {
697         this->WriteBuildEvents(*fout);
698       }
699     }
700   }
701
702   for (cmGeneratedFileStream* f : gfiles) {
703     f->Close();
704   }
705 }
706
707 void cmGhsMultiTargetGenerator::WriteCustomCommandLine(
708   std::ostream& fout, std::string& fname, cmCustomCommandGenerator const& ccg)
709 {
710   /* NOTE: Customization Files are not well documented.  Testing showed
711    * that ":outputName=file" can only be used once per script.  The
712    * script will only run if ":outputName=file" is missing or just run
713    * once if ":outputName=file" is not specified.  If there are
714    * multiple outputs then the script needs to be listed multiple times
715    * for each output.  Otherwise it won't rerun the script if one of
716    * the outputs is manually deleted.
717    */
718   bool specifyExtra = true;
719   for (const auto& out : ccg.GetOutputs()) {
720     fout << fname << '\n';
721     fout << "    :outputName=\"" << out << "\"\n";
722     if (specifyExtra) {
723       for (const auto& byp : ccg.GetByproducts()) {
724         fout << "    :extraOutputFile=\"" << byp << "\"\n";
725       }
726       for (const auto& dep : ccg.GetDepends()) {
727         fout << "    :depends=\"" << dep << "\"\n";
728       }
729       specifyExtra = false;
730     }
731   }
732 }
733
734 std::string cmGhsMultiTargetGenerator::WriteObjectLangOverride(
735   const cmSourceFile* sourceFile)
736 {
737   std::string ret;
738   cmValue rawLangProp = sourceFile->GetProperty("LANGUAGE");
739   if (rawLangProp) {
740     ret = cmStrCat(" [", *rawLangProp, "]");
741   }
742
743   return ret;
744 }
745
746 bool cmGhsMultiTargetGenerator::DetermineIfIntegrityApp()
747 {
748   if (cmValue p = this->GeneratorTarget->GetProperty("ghs_integrity_app")) {
749     return cmIsOn(*p);
750   }
751   std::vector<cmSourceFile*> sources;
752   this->GeneratorTarget->GetSourceFiles(sources, this->ConfigName);
753   return std::any_of(sources.begin(), sources.end(),
754                      [](cmSourceFile const* sf) -> bool {
755                        return "int" == sf->GetExtension();
756                      });
757 }
758
759 bool cmGhsMultiTargetGenerator::ComputeCustomCommandOrder(
760   std::vector<cmSourceFile const*>& order)
761 {
762   std::set<cmSourceFile const*> temp;
763   std::set<cmSourceFile const*> perm;
764
765   // Collect all custom commands for this target
766   std::vector<cmSourceFile const*> customCommands;
767   this->GeneratorTarget->GetCustomCommands(customCommands, this->ConfigName);
768
769   for (cmSourceFile const* si : customCommands) {
770     bool r = this->VisitCustomCommand(temp, perm, order, si);
771     if (r) {
772       return r;
773     }
774   }
775   return false;
776 }
777
778 bool cmGhsMultiTargetGenerator::VisitCustomCommand(
779   std::set<cmSourceFile const*>& temp, std::set<cmSourceFile const*>& perm,
780   std::vector<cmSourceFile const*>& order, cmSourceFile const* si)
781 {
782   /* check if permanent mark is set*/
783   if (perm.find(si) == perm.end()) {
784     /* set temporary mark; check if revisit*/
785     if (temp.insert(si).second) {
786       for (const auto& di : si->GetCustomCommand()->GetDepends()) {
787         cmSourceFile const* sf =
788           this->GeneratorTarget->GetLocalGenerator()->GetSourceFileWithOutput(
789             di);
790         /* if sf exists then visit */
791         if (sf && this->VisitCustomCommand(temp, perm, order, sf)) {
792           return true;
793         }
794       }
795       /* mark as complete; insert into beginning of list*/
796       perm.insert(si);
797       order.push_back(si);
798       return false;
799     }
800     /* revisiting item - not a DAG */
801     return true;
802   }
803   /* already complete */
804   return false;
805 }