Coverage Report

Created: 2026-04-29 07:01

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