Coverage Report

Created: 2026-07-30 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmCxxModuleUsageEffects.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 "cmCxxModuleUsageEffects.h"
4
5
#include <algorithm>
6
#include <set>
7
#include <utility>
8
#include <vector>
9
10
#include <cm/string_view>
11
#include <cmext/string_view>
12
13
#include "cmCryptoHash.h"
14
#include "cmGeneratorTarget.h"
15
#include "cmListFileCache.h"
16
#include "cmMakefile.h"
17
#include "cmStringAlgorithms.h"
18
#include "cmTarget.h"
19
#include "cmValue.h"
20
21
namespace {
22
23
bool IsMSVCWarning(cm::string_view flag)
24
0
{
25
0
  if (flag.size() < 2) {
26
0
    return false;
27
0
  }
28
29
0
  if (flag.front() != '/' && flag.front() != '-') {
30
0
    return false;
31
0
  }
32
33
0
  flag.remove_prefix(1);
34
35
0
  if (flag.front() == 'w' || flag.front() == 'W') {
36
0
    return true;
37
0
  }
38
39
0
  if (flag.substr(0, 9) == "external:") {
40
0
    return true;
41
0
  }
42
43
0
  return false;
44
0
}
45
46
bool IsGNUWarning(cm::string_view flag)
47
0
{
48
0
  if (flag.size() < 2) {
49
0
    return false;
50
0
  }
51
52
0
  if (flag.front() != '-') {
53
0
    return false;
54
0
  }
55
56
0
  flag.remove_prefix(1);
57
58
0
  if (flag.front() == 'w' || flag.front() == 'W') {
59
0
    return true;
60
0
  }
61
62
0
  if (flag.front() == '-') {
63
0
    flag.remove_prefix(1);
64
0
  }
65
66
0
  static std::set<cm::string_view> const long_names{
67
0
    "pedantic", "pedantic-errors", "all-warnings", "extra-warnings",
68
0
    "no-warnings"
69
0
  };
70
71
0
  return long_names.find(flag) != long_names.end();
72
0
}
73
74
void AppendUsage(std::string& usageHashInput, std::string const& entry)
75
0
{
76
0
  usageHashInput += entry;
77
0
  usageHashInput.push_back('\0');
78
0
}
79
80
template <typename Range>
81
void AppendUsageEntries(std::string& usageHashInput, Range const& entries)
82
0
{
83
0
  std::vector<std::string> entryValues;
84
0
  entryValues.reserve(entries.size());
85
0
  for (auto const& entry : entries) {
86
0
    entryValues.push_back(entry.Value);
87
0
  }
88
0
  std::sort(entryValues.begin(), entryValues.end());
89
90
0
  for (auto const& entryValue : entryValues) {
91
0
    usageHashInput += entryValue;
92
0
    usageHashInput.push_back('\n');
93
0
  }
94
95
0
  usageHashInput.push_back('\0');
96
0
}
Unexecuted instantiation: cmCxxModuleUsageEffects.cxx:void (anonymous namespace)::AppendUsageEntries<cmRange<std::__1::__wrap_iter<BT<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > const*> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, cmRange<std::__1::__wrap_iter<BT<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > const*> > const&)
Unexecuted instantiation: cmCxxModuleUsageEffects.cxx:void (anonymous namespace)::AppendUsageEntries<std::__1::vector<BT<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<BT<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::vector<BT<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<BT<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > > const&)
97
98
enum class CompileOptionUsage
99
{
100
  Hash,
101
  Ignore,
102
  SavePreprocessor,
103
};
104
105
struct CompileOptionClassification
106
{
107
  CompileOptionClassification(
108
    CompileOptionUsage usage = CompileOptionUsage::Hash,
109
    bool saveFollowingArgument = false)
110
0
    : Usage(usage)
111
0
    , SaveFollowingArgument(saveFollowingArgument)
112
0
  {
113
0
  }
114
115
  CompileOptionUsage Usage = CompileOptionUsage::Hash;
116
  bool SaveFollowingArgument = false;
117
};
118
119
CompileOptionClassification SaveIfStartsWith(cm::string_view flag,
120
                                             cm::string_view prefix)
121
0
{
122
0
  if (flag.rfind(prefix, 0) == 0) {
123
0
    return { CompileOptionUsage::SavePreprocessor,
124
0
             flag.size() == prefix.size() };
125
0
  }
126
127
0
  return {};
128
0
}
129
130
CompileOptionClassification ClassifyGNUCompileOption(cm::string_view flag)
131
0
{
132
0
  if (IsGNUWarning(flag)) {
133
0
    return { CompileOptionUsage::Ignore, false };
134
0
  }
135
136
0
  if (flag.size() < 2 || flag.front() != '-') {
137
0
    return {};
138
0
  }
139
140
0
  flag.remove_prefix(1);
141
0
  switch (flag.front()) {
142
0
    case 'D':
143
0
    case 'I':
144
0
    case 'U':
145
0
      return { CompileOptionUsage::SavePreprocessor, flag.size() == 1 };
146
0
    case 'i': {
147
0
      CompileOptionClassification classification =
148
0
        SaveIfStartsWith(flag, "isystem"_s);
149
0
      if (classification.Usage != CompileOptionUsage::Hash) {
150
0
        return classification;
151
0
      }
152
0
    }
153
0
      {
154
0
        CompileOptionClassification classification =
155
0
          SaveIfStartsWith(flag, "iquote"_s);
156
0
        if (classification.Usage != CompileOptionUsage::Hash) {
157
0
          return classification;
158
0
        }
159
0
      }
160
0
      {
161
0
        CompileOptionClassification classification =
162
0
          SaveIfStartsWith(flag, "idirafter"_s);
163
0
        if (classification.Usage != CompileOptionUsage::Hash) {
164
0
          return classification;
165
0
        }
166
0
      }
167
0
      {
168
0
        CompileOptionClassification classification =
169
0
          SaveIfStartsWith(flag, "include"_s);
170
0
        if (classification.Usage != CompileOptionUsage::Hash) {
171
0
          return classification;
172
0
        }
173
0
      }
174
0
      {
175
0
        CompileOptionClassification classification =
176
0
          SaveIfStartsWith(flag, "imacros"_s);
177
0
        if (classification.Usage != CompileOptionUsage::Hash) {
178
0
          return classification;
179
0
        }
180
0
      }
181
0
      break;
182
0
    case '-':
183
0
      flag.remove_prefix(1);
184
0
      return SaveIfStartsWith(flag, "embed-dir"_s);
185
0
    default:
186
0
      break;
187
0
  }
188
189
0
  return {};
190
0
}
191
192
CompileOptionClassification ClassifyMSVCCompileOption(cm::string_view flag)
193
0
{
194
0
  if (IsMSVCWarning(flag)) {
195
0
    return { CompileOptionUsage::Ignore, false };
196
0
  }
197
198
0
  if (flag.size() < 2 || (flag.front() != '/' && flag.front() != '-')) {
199
0
    return {};
200
0
  }
201
202
0
  flag.remove_prefix(1);
203
0
  switch (flag.front()) {
204
0
    case 'D':
205
0
    case 'I':
206
0
    case 'U':
207
0
      return { CompileOptionUsage::SavePreprocessor, flag.size() == 1 };
208
0
    default:
209
0
      break;
210
0
  }
211
212
0
  return {};
213
0
}
214
215
CompileOptionClassification ClassifyUnknownCompileOption(cm::string_view flag)
216
0
{
217
0
  static_cast<void>(flag);
218
0
  return {};
219
0
}
220
221
using CompileOptionClassifier =
222
  CompileOptionClassification (*)(cm::string_view flag);
223
224
CompileOptionClassifier GetCompileOptionClassifier(cmGeneratorTarget const* gt)
225
0
{
226
0
  static CompileOptionClassifier classifier = nullptr;
227
0
  if (classifier) {
228
0
    return classifier;
229
0
  }
230
231
0
  cmValue frontendVariant =
232
0
    gt->Makefile->GetDefinition("CMAKE_CXX_COMPILER_FRONTEND_VARIANT");
233
234
0
  if (frontendVariant == "GNU") {
235
0
    classifier = ClassifyGNUCompileOption;
236
0
  } else if (frontendVariant == "MSVC") {
237
0
    classifier = ClassifyMSVCCompileOption;
238
0
  } else {
239
0
    classifier = ClassifyUnknownCompileOption;
240
0
  }
241
242
0
  return classifier;
243
0
}
244
245
struct SplitCompileOptionsResult
246
{
247
  std::vector<BT<std::string>> HashEntries;
248
  std::vector<BT<std::string>> PreprocessorEntries;
249
};
250
251
template <typename Range>
252
SplitCompileOptionsResult SplitCompileOptions(Range const& entries,
253
                                              CompileOptionClassifier classify)
254
0
{
255
0
  SplitCompileOptionsResult result;
256
257
0
  for (auto it = entries.begin(); it != entries.end(); ++it) {
258
0
    BT<std::string> const& option = *it;
259
0
    auto const classification = classify(option.Value);
260
261
0
    switch (classification.Usage) {
262
0
      case CompileOptionUsage::Ignore:
263
0
        continue;
264
0
      case CompileOptionUsage::SavePreprocessor:
265
0
        result.PreprocessorEntries.push_back(option);
266
0
        if (classification.SaveFollowingArgument) {
267
0
          auto next = it;
268
0
          ++next;
269
0
          if (next != entries.end()) {
270
0
            result.PreprocessorEntries.push_back(*next);
271
0
            it = next;
272
0
          }
273
0
        }
274
0
        continue;
275
0
      case CompileOptionUsage::Hash:
276
0
        break;
277
0
    }
278
279
0
    result.HashEntries.push_back(option);
280
0
  }
281
282
0
  return result;
283
0
}
Unexecuted instantiation: cmCxxModuleUsageEffects.cxx:(anonymous namespace)::SplitCompileOptionsResult (anonymous namespace)::SplitCompileOptions<cmRange<std::__1::__wrap_iter<BT<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > const*> > >(cmRange<std::__1::__wrap_iter<BT<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > const*> > const&, (anonymous namespace)::CompileOptionClassification (*)(std::__1::basic_string_view<char, std::__1::char_traits<char> >))
Unexecuted instantiation: cmCxxModuleUsageEffects.cxx:(anonymous namespace)::SplitCompileOptionsResult (anonymous namespace)::SplitCompileOptions<std::__1::vector<BT<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<BT<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > > >(std::__1::vector<BT<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<BT<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > > const&, (anonymous namespace)::CompileOptionClassification (*)(std::__1::basic_string_view<char, std::__1::char_traits<char> >))
284
}
285
286
cmCxxModuleUsageEffects::cmCxxModuleUsageEffects(cmGeneratorTarget const* gt,
287
                                                 std::string const& config)
288
0
{
289
0
  auto const* tgt = gt->Target;
290
0
  auto const classify = GetCompileOptionClassifier(gt);
291
292
0
  std::string usageHashInput;
293
0
  if (tgt->IsImported()) {
294
0
    AppendUsageEntries(usageHashInput,
295
0
                       tgt->GetImportedCxxModulesCompileFeaturesEntries());
296
0
    auto compileOptions = SplitCompileOptions(
297
0
      tgt->GetImportedCxxModulesCompileOptionsEntries(), classify);
298
0
    this->PreprocessorCompileOptions =
299
0
      std::move(compileOptions.PreprocessorEntries);
300
0
    AppendUsageEntries(usageHashInput, compileOptions.HashEntries);
301
0
  } else {
302
0
    auto compileOptions =
303
0
      SplitCompileOptions(gt->GetCompileOptions(config, "CXX"), classify);
304
0
    this->PreprocessorCompileOptions =
305
0
      std::move(compileOptions.PreprocessorEntries);
306
0
    AppendUsageEntries(usageHashInput, compileOptions.HashEntries);
307
308
0
    cmValue langStd = gt->GetLanguageStandard("CXX", config);
309
0
    if (!langStd) {
310
0
      langStd = gt->Makefile->GetDefinition("CMAKE_CXX_STANDARD_DEFAULT");
311
0
    }
312
0
    AppendUsage(usageHashInput,
313
0
                cmStrCat("CXX_STANDARD:", langStd ? *langStd : "20"));
314
0
    AppendUsage(
315
0
      usageHashInput,
316
0
      cmStrCat("CXX_EXTENSIONS:", *gt->GetLanguageExtensions("CXX")));
317
0
    AppendUsage(
318
0
      usageHashInput,
319
0
      cmStrCat("CXX_STANDARD_REQUIRED:",
320
0
               gt->GetLanguageStandardRequired("CXX") ? "TRUE" : "FALSE"));
321
0
  }
322
323
0
  cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512);
324
0
  this->Hash = hasher.HashString(usageHashInput);
325
0
}
326
327
std::string const& cmCxxModuleUsageEffects::GetHash() const
328
0
{
329
0
  return this->Hash;
330
0
}
331
332
std::vector<BT<std::string>> const&
333
cmCxxModuleUsageEffects::GetPreprocessorCompileOptions() const
334
0
{
335
0
  return this->PreprocessorCompileOptions;
336
0
}