Coverage Report

Created: 2026-03-12 06:35

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