Coverage Report

Created: 2026-02-09 06:05

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