Coverage Report

Created: 2026-04-29 07:01

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