Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmScriptGenerator.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 "cmScriptGenerator.h"
4
5
#include <algorithm>
6
#include <utility>
7
8
#include "cmStringAlgorithms.h"
9
#include "cmSystemTools.h"
10
11
cmScriptGenerator::cmScriptGenerator(std::string config_var,
12
                                     std::vector<std::string> configurations)
13
0
  : RuntimeConfigVariable(std::move(config_var))
14
0
  , Configurations(std::move(configurations))
15
0
{
16
0
}
17
18
0
cmScriptGenerator::~cmScriptGenerator() = default;
19
20
void cmScriptGenerator::Generate(
21
  std::ostream& os, std::string const& config,
22
  std::vector<std::string> const& configurationTypes)
23
0
{
24
0
  this->ConfigurationName = config;
25
0
  this->ConfigurationTypes = &configurationTypes;
26
0
  this->GenerateScript(os);
27
0
  this->ConfigurationName.clear();
28
0
  this->ConfigurationTypes = nullptr;
29
0
}
30
31
static void cmScriptGeneratorEncodeConfig(std::string const& config,
32
                                          std::string& result)
33
0
{
34
0
  for (char const* c = config.c_str(); *c; ++c) {
35
0
    if (*c >= 'a' && *c <= 'z') {
36
0
      result += "[";
37
0
      result += static_cast<char>(*c + 'A' - 'a');
38
0
      result += *c;
39
0
      result += "]";
40
0
    } else if (*c >= 'A' && *c <= 'Z') {
41
0
      result += "[";
42
0
      result += *c;
43
0
      result += static_cast<char>(*c + 'a' - 'A');
44
0
      result += "]";
45
0
    } else {
46
0
      result += *c;
47
0
    }
48
0
  }
49
0
}
50
51
std::string cmScriptGenerator::CreateConfigTest(std::string const& config)
52
0
{
53
0
  std::string result = cmStrCat(this->RuntimeConfigVariable, " MATCHES \"^(");
54
0
  if (!config.empty()) {
55
0
    cmScriptGeneratorEncodeConfig(config, result);
56
0
  }
57
0
  result += ")$\"";
58
0
  return result;
59
0
}
60
61
std::string cmScriptGenerator::CreateConfigTest(
62
  std::vector<std::string> const& configs)
63
0
{
64
0
  std::string result = cmStrCat(this->RuntimeConfigVariable, " MATCHES \"^(");
65
0
  char const* sep = "";
66
0
  for (std::string const& config : configs) {
67
0
    result += sep;
68
0
    sep = "|";
69
0
    cmScriptGeneratorEncodeConfig(config, result);
70
0
  }
71
0
  result += ")$\"";
72
0
  return result;
73
0
}
74
75
void cmScriptGenerator::GenerateScript(std::ostream& os)
76
0
{
77
  // Track indentation.
78
0
  Indent indent;
79
80
  // Generate the script possibly with per-configuration code.
81
0
  this->GenerateScriptConfigs(os, indent);
82
0
}
83
84
void cmScriptGenerator::GenerateScriptConfigs(std::ostream& os, Indent indent)
85
0
{
86
0
  if (this->ActionsPerConfig) {
87
0
    this->GenerateScriptActionsPerConfig(os, indent);
88
0
  } else {
89
0
    this->GenerateScriptActionsOnce(os, indent);
90
0
  }
91
0
}
92
93
void cmScriptGenerator::GenerateScriptActions(std::ostream& os, Indent indent)
94
0
{
95
0
  if (this->ActionsPerConfig) {
96
    // This is reached for single-configuration build generators in a
97
    // per-config script generator.
98
0
    this->GenerateScriptForConfig(os, this->ConfigurationName, indent);
99
0
  }
100
0
}
101
102
void cmScriptGenerator::GenerateScriptForConfig(std::ostream& /*unused*/,
103
                                                std::string const& /*unused*/,
104
                                                Indent /*unused*/)
105
0
{
106
  // No actions for this generator.
107
0
}
108
109
bool cmScriptGenerator::GeneratesForConfig(std::string const& config)
110
0
{
111
  // If this is not a configuration-specific rule then we install.
112
0
  if (this->Configurations.empty()) {
113
0
    return true;
114
0
  }
115
116
  // This is a configuration-specific rule.  Check if the config
117
  // matches this rule.
118
0
  std::string config_upper = cmSystemTools::UpperCase(config);
119
0
  return std::any_of(this->Configurations.begin(), this->Configurations.end(),
120
0
                     [&config_upper](std::string const& cfg) -> bool {
121
0
                       return cmSystemTools::UpperCase(cfg) == config_upper;
122
0
                     });
123
0
}
124
125
void cmScriptGenerator::GenerateScriptActionsOnce(std::ostream& os,
126
                                                  Indent indent)
127
0
{
128
0
  if (this->Configurations.empty()) {
129
    // This rule is for all configurations.
130
0
    this->GenerateScriptActions(os, indent);
131
0
  } else {
132
    // Generate a per-configuration block.
133
0
    std::string config_test = this->CreateConfigTest(this->Configurations);
134
0
    os << indent << "if(" << config_test << ")\n";
135
0
    this->GenerateScriptActions(os, indent.Next());
136
0
    os << indent << "endif()\n";
137
0
  }
138
0
}
139
140
void cmScriptGenerator::GenerateScriptActionsPerConfig(std::ostream& os,
141
                                                       Indent indent)
142
0
{
143
0
  if (this->ConfigurationTypes->empty()) {
144
    // In a single-configuration generator there is only one action
145
    // and it applies if the runtime-requested configuration is among
146
    // the rule's allowed configurations.  The configuration built in
147
    // the tree does not matter for this decision but will be used to
148
    // generate proper target file names into the code.
149
0
    this->GenerateScriptActionsOnce(os, indent);
150
0
  } else {
151
    // In a multi-configuration generator we produce a separate rule
152
    // in a block for each configuration that is built.  We restrict
153
    // the list of configurations to those to which this rule applies.
154
0
    bool first = true;
155
0
    for (std::string const& cfgType : *this->ConfigurationTypes) {
156
0
      if (this->GeneratesForConfig(cfgType)) {
157
        // Generate a per-configuration block.
158
0
        std::string config_test = this->CreateConfigTest(cfgType);
159
0
        os << indent << (first ? "if(" : "elseif(") << config_test << ")\n";
160
0
        this->GenerateScriptForConfig(os, cfgType, indent.Next());
161
0
        first = false;
162
0
      }
163
0
    }
164
0
    if (!first) {
165
0
      if (this->NeedsScriptNoConfig()) {
166
0
        os << indent << "else()\n";
167
0
        this->GenerateScriptNoConfig(os, indent.Next());
168
0
      }
169
0
      os << indent << "endif()\n";
170
0
    }
171
0
  }
172
0
}