Coverage Report

Created: 2026-10-28 06:38

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