Coverage Report

Created: 2026-07-14 07:09

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