Coverage Report

Created: 2026-03-12 06:35

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 <cstddef>
7
#include <sstream>
8
#include <utility>
9
10
#include "cmStringAlgorithms.h"
11
#include "cmSystemTools.h"
12
13
namespace {
14
15
int RequiredBracketLength(cm::string_view s)
16
0
{
17
0
  for (int n = 0;; ++n) {
18
0
    std::string const needle = "]" + std::string(n, '=');
19
20
0
    bool ok = true;
21
0
    std::size_t pos = s.find(needle);
22
0
    while (pos != cm::string_view::npos) {
23
0
      std::size_t const next = pos + needle.size();
24
0
      if (next == s.size() || s[next] == ']') {
25
0
        ok = false;
26
0
        break;
27
0
      }
28
0
      pos = s.find(needle, pos + 1);
29
0
    }
30
31
0
    if (ok) {
32
0
      return n;
33
0
    }
34
0
  }
35
0
}
36
37
} // namespace
38
39
std::ostream& operator<<(std::ostream& os, cmScriptGeneratorQuoted const& self)
40
0
{
41
0
  if (self.BracketLength == -1) {
42
0
    return os << '"' << self.Value << '"';
43
0
  }
44
0
  auto const sep = std::string(self.BracketLength, '=');
45
0
  return os << '[' << sep << '[' << self.Value << ']' << sep << ']';
46
0
}
47
48
std::string cmScriptGeneratorQuoted::str() const
49
0
{
50
0
  std::stringstream out;
51
0
  out << *this;
52
0
  return out.str();
53
0
}
54
55
cmScriptGenerator::cmScriptGenerator(std::string config_var,
56
                                     std::vector<std::string> configurations)
57
0
  : RuntimeConfigVariable(std::move(config_var))
58
0
  , Configurations(std::move(configurations))
59
0
{
60
0
}
61
62
0
cmScriptGenerator::~cmScriptGenerator() = default;
63
64
void cmScriptGenerator::Generate(
65
  std::ostream& os, std::string const& config,
66
  std::vector<std::string> const& configurationTypes)
67
0
{
68
0
  this->ConfigurationName = config;
69
0
  this->ConfigurationTypes = &configurationTypes;
70
0
  this->GenerateScript(os);
71
0
  this->ConfigurationName.clear();
72
0
  this->ConfigurationTypes = nullptr;
73
0
}
74
75
cmScriptGeneratorQuoted cmScriptGenerator::Quote(cm::string_view value)
76
0
{
77
0
  if (value.find_first_of("\"$\\") == std::string::npos) {
78
0
    return cmScriptGeneratorQuoted(value, -1);
79
0
  }
80
0
  return cmScriptGeneratorQuoted(value, RequiredBracketLength(value));
81
0
}
82
83
static void cmScriptGeneratorEncodeConfig(std::string const& config,
84
                                          std::string& result)
85
0
{
86
0
  for (char const* c = config.c_str(); *c; ++c) {
87
0
    if (*c >= 'a' && *c <= 'z') {
88
0
      result += "[";
89
0
      result += static_cast<char>(*c + 'A' - 'a');
90
0
      result += *c;
91
0
      result += "]";
92
0
    } else if (*c >= 'A' && *c <= 'Z') {
93
0
      result += "[";
94
0
      result += *c;
95
0
      result += static_cast<char>(*c + 'a' - 'A');
96
0
      result += "]";
97
0
    } else {
98
0
      result += *c;
99
0
    }
100
0
  }
101
0
}
102
103
std::string cmScriptGenerator::CreateConfigTest(std::string const& config)
104
0
{
105
0
  std::string result = cmStrCat(this->RuntimeConfigVariable, " MATCHES \"^(");
106
0
  if (!config.empty()) {
107
0
    cmScriptGeneratorEncodeConfig(config, result);
108
0
  }
109
0
  result += ")$\"";
110
0
  return result;
111
0
}
112
113
std::string cmScriptGenerator::CreateConfigTest(
114
  std::vector<std::string> const& configs)
115
0
{
116
0
  std::string result = cmStrCat(this->RuntimeConfigVariable, " MATCHES \"^(");
117
0
  char const* sep = "";
118
0
  for (std::string const& config : configs) {
119
0
    result += sep;
120
0
    sep = "|";
121
0
    cmScriptGeneratorEncodeConfig(config, result);
122
0
  }
123
0
  result += ")$\"";
124
0
  return result;
125
0
}
126
127
void cmScriptGenerator::GenerateScript(std::ostream& os)
128
0
{
129
  // Track indentation.
130
0
  Indent indent;
131
132
  // Generate the script possibly with per-configuration code.
133
0
  this->GenerateScriptConfigs(os, indent);
134
0
}
135
136
void cmScriptGenerator::GenerateScriptConfigs(std::ostream& os, Indent indent)
137
0
{
138
0
  if (this->ActionsPerConfig) {
139
0
    this->GenerateScriptActionsPerConfig(os, indent);
140
0
  } else {
141
0
    this->GenerateScriptActionsOnce(os, indent);
142
0
  }
143
0
}
144
145
void cmScriptGenerator::GenerateScriptActions(std::ostream& os, Indent indent)
146
0
{
147
0
  if (this->ActionsPerConfig) {
148
    // This is reached for single-configuration build generators in a
149
    // per-config script generator.
150
0
    this->GenerateScriptForConfig(os, this->ConfigurationName, indent);
151
0
  }
152
0
}
153
154
void cmScriptGenerator::GenerateScriptForConfig(std::ostream& /*unused*/,
155
                                                std::string const& /*unused*/,
156
                                                Indent /*unused*/)
157
0
{
158
  // No actions for this generator.
159
0
}
160
161
bool cmScriptGenerator::GeneratesForConfig(std::string const& config)
162
0
{
163
  // If this is not a configuration-specific rule then we install.
164
0
  if (this->Configurations.empty()) {
165
0
    return true;
166
0
  }
167
168
  // This is a configuration-specific rule.  Check if the config
169
  // matches this rule.
170
0
  std::string config_upper = cmSystemTools::UpperCase(config);
171
0
  return std::any_of(this->Configurations.begin(), this->Configurations.end(),
172
0
                     [&config_upper](std::string const& cfg) -> bool {
173
0
                       return cmSystemTools::UpperCase(cfg) == config_upper;
174
0
                     });
175
0
}
176
177
void cmScriptGenerator::GenerateScriptActionsOnce(std::ostream& os,
178
                                                  Indent indent)
179
0
{
180
0
  if (this->Configurations.empty()) {
181
    // This rule is for all configurations.
182
0
    this->GenerateScriptActions(os, indent);
183
0
  } else {
184
    // Generate a per-configuration block.
185
0
    std::string config_test = this->CreateConfigTest(this->Configurations);
186
0
    os << indent << "if(" << config_test << ")\n";
187
0
    this->GenerateScriptActions(os, indent.Next());
188
0
    os << indent << "endif()\n";
189
0
  }
190
0
}
191
192
void cmScriptGenerator::GenerateScriptActionsPerConfig(std::ostream& os,
193
                                                       Indent indent)
194
0
{
195
0
  if (this->ConfigurationTypes->empty()) {
196
    // In a single-configuration generator there is only one action
197
    // and it applies if the runtime-requested configuration is among
198
    // the rule's allowed configurations.  The configuration built in
199
    // the tree does not matter for this decision but will be used to
200
    // generate proper target file names into the code.
201
0
    this->GenerateScriptActionsOnce(os, indent);
202
0
  } else {
203
    // In a multi-configuration generator we produce a separate rule
204
    // in a block for each configuration that is built.  We restrict
205
    // the list of configurations to those to which this rule applies.
206
0
    bool first = true;
207
0
    for (std::string const& cfgType : *this->ConfigurationTypes) {
208
0
      if (this->GeneratesForConfig(cfgType)) {
209
        // Generate a per-configuration block.
210
0
        std::string config_test = this->CreateConfigTest(cfgType);
211
0
        os << indent << (first ? "if(" : "elseif(") << config_test << ")\n";
212
0
        this->GenerateScriptForConfig(os, cfgType, indent.Next());
213
0
        first = false;
214
0
      }
215
0
    }
216
0
    if (!first) {
217
0
      if (this->NeedsScriptNoConfig()) {
218
0
        os << indent << "else()\n";
219
0
        this->GenerateScriptNoConfig(os, indent.Next());
220
0
      }
221
0
      os << indent << "endif()\n";
222
0
    }
223
0
  }
224
0
}