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