Coverage Report

Created: 2026-10-28 06:38

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