Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmPolicies.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 "cmPolicies.h"
4
5
#include <cassert>
6
#include <cctype>
7
#include <cstdio>
8
#include <cstring>
9
#include <sstream>
10
#include <vector>
11
12
#include "cmListFileCache.h"
13
#include "cmMakefile.h"
14
#include "cmMessageType.h"
15
#include "cmStateSnapshot.h"
16
#include "cmStringAlgorithms.h"
17
#include "cmSystemTools.h"
18
#include "cmValue.h"
19
#include "cmVersion.h"
20
21
static bool stringToId(char const* input, cmPolicies::PolicyID& pid)
22
0
{
23
0
  assert(input);
24
0
  if (strlen(input) != 7) {
25
0
    return false;
26
0
  }
27
0
  if (!cmHasLiteralPrefix(input, "CMP")) {
28
0
    return false;
29
0
  }
30
0
  if (cmHasLiteralSuffix(input, "0000")) {
31
0
    pid = cmPolicies::CMP0000;
32
0
    return true;
33
0
  }
34
0
  for (int i = 3; i < 7; ++i) {
35
0
    if (!isdigit(*(input + i))) {
36
0
      return false;
37
0
    }
38
0
  }
39
0
  long id;
40
0
  if (!cmStrToLong(input + 3, &id)) {
41
0
    return false;
42
0
  }
43
0
  if (id >= cmPolicies::CMPCOUNT) {
44
0
    return false;
45
0
  }
46
0
  pid = static_cast<cmPolicies::PolicyID>(id);
47
0
  return true;
48
0
}
49
50
0
#define CM_SELECT_ID_VERSION(F, A1, A2, A3, A4, A5, A6) F(A1, A3, A4, A5)
51
#define CM_FOR_EACH_POLICY_ID_VERSION(POLICY)                                 \
52
0
  CM_FOR_EACH_POLICY_TABLE(POLICY, CM_SELECT_ID_VERSION)
53
54
0
#define CM_SELECT_ID_DOC(F, A1, A2, A3, A4, A5, A6) F(A1, A2)
55
#define CM_FOR_EACH_POLICY_ID_DOC(POLICY)                                     \
56
0
  CM_FOR_EACH_POLICY_TABLE(POLICY, CM_SELECT_ID_DOC)
57
58
0
#define CM_SELECT_ID_STATUS(F, A1, A2, A3, A4, A5, A6) F(A1, A6)
59
#define CM_FOR_EACH_POLICY_ID_STATUS(POLICY)                                  \
60
0
  CM_FOR_EACH_POLICY_TABLE(POLICY, CM_SELECT_ID_STATUS)
61
62
static char const* idToString(cmPolicies::PolicyID id)
63
0
{
64
0
  switch (id) {
65
0
#define POLICY_CASE(ID)                                                       \
66
0
  case cmPolicies::ID:                                                        \
67
0
    return #ID;
68
0
    CM_FOR_EACH_POLICY_ID(POLICY_CASE)
69
0
#undef POLICY_CASE
70
0
    case cmPolicies::CMPCOUNT:
71
0
      return nullptr;
72
0
  }
73
0
  return nullptr;
74
0
}
75
76
static char const* idToVersion(cmPolicies::PolicyID id)
77
0
{
78
0
  switch (id) {
79
0
#define POLICY_CASE(ID, V_MAJOR, V_MINOR, V_PATCH)                            \
80
0
  case cmPolicies::ID:                                                        \
81
0
    return #V_MAJOR "." #V_MINOR "." #V_PATCH;
82
    // NOLINTNEXTLINE(bugprone-branch-clone)
83
0
    CM_FOR_EACH_POLICY_ID_VERSION(POLICY_CASE)
84
0
#undef POLICY_CASE
85
0
    case cmPolicies::CMPCOUNT:
86
0
      return nullptr;
87
0
  }
88
0
  return nullptr;
89
0
}
90
91
static bool isPolicyNewerThan(cmPolicies::PolicyID id, unsigned int majorV,
92
                              unsigned int minorV, unsigned int patchV)
93
0
{
94
0
  switch (id) {
95
0
#define POLICY_CASE(ID, V_MAJOR, V_MINOR, V_PATCH)                            \
96
0
  case cmPolicies::ID:                                                        \
97
0
    return (majorV < (V_MAJOR) ||                                             \
98
0
            (majorV == (V_MAJOR) && minorV + 1 < (V_MINOR) + 1) ||            \
99
0
            (majorV == (V_MAJOR) && minorV == (V_MINOR) &&                    \
100
0
             patchV + 1 < (V_PATCH) + 1));
101
    // NOLINTNEXTLINE(bugprone-branch-clone)
102
0
    CM_FOR_EACH_POLICY_ID_VERSION(POLICY_CASE)
103
0
#undef POLICY_CASE
104
0
    case cmPolicies::CMPCOUNT:
105
0
      return false;
106
0
  }
107
0
  return false;
108
0
}
109
110
static char const* idToShortDescription(cmPolicies::PolicyID id)
111
0
{
112
0
  switch (id) {
113
0
#define POLICY_CASE(ID, SHORT_DESCRIPTION)                                    \
114
0
  case cmPolicies::ID:                                                        \
115
0
    return SHORT_DESCRIPTION;
116
0
    CM_FOR_EACH_POLICY_ID_DOC(POLICY_CASE)
117
0
#undef POLICY_CASE
118
0
    case cmPolicies::CMPCOUNT:
119
0
      return nullptr;
120
0
  }
121
0
  return nullptr;
122
0
}
123
124
namespace {
125
cmPolicies::PolicyStatus idToStatus(cmPolicies::PolicyID id)
126
0
{
127
0
  switch (id) {
128
0
#define POLICY_CASE(ID, STATUS)                                               \
129
0
  case cmPolicies::ID:                                                        \
130
0
    return cmPolicies::STATUS;
131
    // NOLINTNEXTLINE(bugprone-branch-clone)
132
0
    CM_FOR_EACH_POLICY_ID_STATUS(POLICY_CASE)
133
0
#undef POLICY_CASE
134
0
    case cmPolicies::CMPCOUNT:
135
0
      break;
136
0
  }
137
0
  return cmPolicies::WARN;
138
0
}
139
}
140
141
static void DiagnoseAncientPolicies(
142
  std::vector<cmPolicies::PolicyID> const& ancient, unsigned int majorVer,
143
  unsigned int minorVer, unsigned int patchVer, cmMakefile* mf)
144
0
{
145
0
  std::ostringstream e;
146
0
  e << "The project requests behavior compatible with CMake version \""
147
0
    << majorVer << '.' << minorVer << '.' << patchVer
148
0
    << "\", which requires the OLD behavior for some policies:\n";
149
0
  for (cmPolicies::PolicyID i : ancient) {
150
0
    e << "  " << idToString(i) << ": " << idToShortDescription(i) << '\n';
151
0
  }
152
0
  e << "However, this version of CMake no longer supports the OLD "
153
0
       "behavior for these policies.  "
154
0
       "Please either update your CMakeLists.txt files to conform to "
155
0
       "the new behavior or use an older version of CMake that still "
156
0
       "supports the old behavior.";
157
0
  mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
158
0
}
159
160
static bool GetPolicyDefault(cmMakefile* mf, std::string const& policy,
161
                             cmPolicies::PolicyStatus* defaultSetting)
162
0
{
163
0
  std::string defaultVar = cmStrCat("CMAKE_POLICY_DEFAULT_", policy);
164
0
  std::string const& defaultValue = mf->GetSafeDefinition(defaultVar);
165
0
  if (defaultValue == "NEW") {
166
0
    *defaultSetting = cmPolicies::NEW;
167
0
  } else if (defaultValue == "OLD") {
168
0
    *defaultSetting = cmPolicies::OLD;
169
0
  } else if (defaultValue.empty()) {
170
0
    *defaultSetting = cmPolicies::WARN;
171
0
  } else {
172
0
    mf->IssueMessage(
173
0
      MessageType::FATAL_ERROR,
174
0
      cmStrCat(defaultVar, " has value \"", defaultValue,
175
0
               R"(" but must be "OLD", "NEW", or "" (empty).)"));
176
0
    return false;
177
0
  }
178
179
0
  return true;
180
0
}
181
182
bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf,
183
                                    std::string const& version_min,
184
                                    std::string const& version_max,
185
                                    WarnCompat warnCompat)
186
0
{
187
  // Parse components of the minimum version.
188
0
  unsigned int minMajor = 2;
189
0
  unsigned int minMinor = 0;
190
0
  unsigned int minPatch = 0;
191
0
  unsigned int minTweak = 0;
192
0
  if (sscanf(version_min.c_str(), "%u.%u.%u.%u", &minMajor, &minMinor,
193
0
             &minPatch, &minTweak) < 2) {
194
0
    mf->IssueMessage(
195
0
      MessageType::FATAL_ERROR,
196
0
      cmStrCat("Invalid policy version value \"", version_min,
197
0
               "\".  "
198
0
               "A numeric major.minor[.patch[.tweak]] must be given."));
199
0
    return false;
200
0
  }
201
202
  // it is an error if the policy version is less than 2.4
203
0
  if (minMajor < 2 || (minMajor == 2 && minMinor < 4)) {
204
0
    mf->IssueMessage(
205
0
      MessageType::FATAL_ERROR,
206
0
      "Compatibility with CMake < 2.4 is not supported by CMake >= 3.0.  "
207
0
      "For compatibility with older versions please use any CMake 2.8.x "
208
0
      "release or lower.");
209
0
    return false;
210
0
  }
211
212
  // It is an error if the policy version is greater than the running
213
  // CMake.
214
0
  if (minMajor > cmVersion::GetMajorVersion() ||
215
0
      (minMajor == cmVersion::GetMajorVersion() &&
216
0
       minMinor > cmVersion::GetMinorVersion()) ||
217
0
      (minMajor == cmVersion::GetMajorVersion() &&
218
0
       minMinor == cmVersion::GetMinorVersion() &&
219
0
       minPatch > cmVersion::GetPatchVersion()) ||
220
0
      (minMajor == cmVersion::GetMajorVersion() &&
221
0
       minMinor == cmVersion::GetMinorVersion() &&
222
0
       minPatch == cmVersion::GetPatchVersion() &&
223
0
       minTweak > cmVersion::GetTweakVersion())) {
224
0
    mf->IssueMessage(
225
0
      MessageType::FATAL_ERROR,
226
0
      cmStrCat("An attempt was made to set the policy version of CMake to \"",
227
0
               version_min,
228
0
               "\" which is greater than this version of CMake.  "
229
0
               "This is not allowed because the greater version may have new "
230
0
               "policies not known to this CMake.  "
231
0
               "You may need a newer CMake version to build this project."));
232
0
    return false;
233
0
  }
234
235
0
  unsigned int polMajor = minMajor;
236
0
  unsigned int polMinor = minMinor;
237
0
  unsigned int polPatch = minPatch;
238
239
0
  if (!version_max.empty()) {
240
    // Parse components of the maximum version.
241
0
    unsigned int maxMajor = 0;
242
0
    unsigned int maxMinor = 0;
243
0
    unsigned int maxPatch = 0;
244
0
    unsigned int maxTweak = 0;
245
0
    if (sscanf(version_max.c_str(), "%u.%u.%u.%u", &maxMajor, &maxMinor,
246
0
               &maxPatch, &maxTweak) < 2) {
247
0
      mf->IssueMessage(
248
0
        MessageType::FATAL_ERROR,
249
0
        cmStrCat("Invalid policy max version value \"", version_max,
250
0
                 "\".  "
251
0
                 "A numeric major.minor[.patch[.tweak]] must be given."));
252
0
      return false;
253
0
    }
254
255
    // It is an error if the min version is greater than the max version.
256
0
    if (minMajor > maxMajor || (minMajor == maxMajor && minMinor > maxMinor) ||
257
0
        (minMajor == maxMajor && minMinor == maxMinor &&
258
0
         minPatch > maxPatch) ||
259
0
        (minMajor == maxMajor && minMinor == maxMinor &&
260
0
         minPatch == maxPatch && minTweak > maxTweak)) {
261
0
      mf->IssueMessage(
262
0
        MessageType::FATAL_ERROR,
263
0
        cmStrCat("Policy VERSION range \"", version_min, "...", version_max,
264
0
                 "\" specifies a larger minimum than maximum."));
265
0
      return false;
266
0
    }
267
268
    // Use the max version as the policy version.
269
0
    polMajor = maxMajor;
270
0
    polMinor = maxMinor;
271
0
    polPatch = maxPatch;
272
0
  }
273
274
0
  return cmPolicies::ApplyPolicyVersion(mf, polMajor, polMinor, polPatch,
275
0
                                        warnCompat);
276
0
}
277
278
namespace {
279
bool IsFromLegacyInstallEXPORT(cmMakefile* mf, unsigned int majorVer,
280
                               unsigned int minorVer, unsigned int patchVer)
281
0
{
282
0
  return majorVer == 2 && minorVer == 6 && patchVer == 0 &&
283
0
    mf->GetStateSnapshot().CanPopPolicyScope() &&
284
0
    cmSystemTools::Strucmp(mf->GetBacktrace().Top().Name.c_str(),
285
0
                           "cmake_policy") == 0;
286
0
}
287
#define ADVICE_UPDATE_VERSION_ARGUMENT                                        \
288
0
  "Update the VERSION argument <min> value.  Or, use the <min>...<max> "      \
289
0
  "syntax to tell CMake that the project requires at least <min> but has "    \
290
0
  "been updated to work with policies introduced by <max> or earlier."
291
}
292
293
bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, unsigned int majorVer,
294
                                    unsigned int minorVer,
295
                                    unsigned int patchVer,
296
                                    WarnCompat warnCompat)
297
0
{
298
0
  cmValue varVer = mf->GetDefinition("CMAKE_POLICY_VERSION_MINIMUM");
299
0
  if (!varVer.IsEmpty()) {
300
0
    unsigned int varMajor = 0;
301
0
    unsigned int varMinor = 0;
302
0
    unsigned int varPatch = 0;
303
0
    unsigned int varTweak = 0;
304
0
    if (sscanf(varVer.GetCStr(), "%u.%u.%u.%u", &varMajor, &varMinor,
305
0
               &varPatch, &varTweak) < 2) {
306
0
      mf->IssueMessage(
307
0
        MessageType::FATAL_ERROR,
308
0
        cmStrCat("Invalid CMAKE_POLICY_VERSION_MINIMUM value \"", varVer,
309
0
                 "\".  "
310
0
                 "A numeric major.minor[.patch[.tweak]] must be given."));
311
0
      return false;
312
0
    }
313
0
    if (varMajor > majorVer || (varMajor == majorVer && varMinor > minorVer) ||
314
0
        (varMajor == majorVer && varMinor == minorVer &&
315
0
         varPatch > patchVer)) {
316
0
      majorVer = varMajor;
317
0
      minorVer = varMinor;
318
0
      patchVer = varPatch;
319
0
    }
320
0
  }
321
322
  // Error on policy versions for which support has been removed.
323
0
  if (majorVer < 3 || (majorVer == 3 && minorVer < 5)) {
324
0
    if (IsFromLegacyInstallEXPORT(mf, majorVer, minorVer, patchVer)) {
325
      // Silently tolerate cmake_policy calls generated by install(EXPORT)
326
      // in CMake versions prior to 3.18.
327
0
      majorVer = 3;
328
0
      minorVer = 5;
329
0
      patchVer = 0;
330
0
    } else {
331
0
      mf->IssueMessage(MessageType::FATAL_ERROR,
332
0
                       "Compatibility with CMake < 3.5 has been removed "
333
0
                       "from CMake.\n" ADVICE_UPDATE_VERSION_ARGUMENT "\n"
334
0
                       "Or, add -DCMAKE_POLICY_VERSION_MINIMUM=3.5 to try "
335
0
                       "configuring anyway.");
336
0
      cmSystemTools::SetFatalErrorOccurred();
337
0
      return false;
338
0
    }
339
0
  } else if (majorVer == 3 && minorVer < 10 && warnCompat == WarnCompat::On) {
340
    // Warn about policy versions for which support will be removed.
341
0
    mf->IssueMessage(
342
0
      MessageType::DEPRECATION_WARNING,
343
0
      "Compatibility with CMake < 3.10 will be removed from "
344
0
      "a future version of CMake.\n" ADVICE_UPDATE_VERSION_ARGUMENT);
345
0
  }
346
347
  // now loop over all the policies and set them as appropriate
348
0
  std::vector<cmPolicies::PolicyID> ancientPolicies;
349
0
  for (PolicyID pid = cmPolicies::CMP0000; pid != cmPolicies::CMPCOUNT;
350
0
       pid = static_cast<PolicyID>(pid + 1)) {
351
0
    if (isPolicyNewerThan(pid, majorVer, minorVer, patchVer)) {
352
0
      if (cmPolicies::IsRemoved(pid)) {
353
0
        ancientPolicies.push_back(pid);
354
0
      } else {
355
0
        cmPolicies::PolicyStatus status = cmPolicies::WARN;
356
0
        if (!GetPolicyDefault(mf, idToString(pid), &status) ||
357
0
            !mf->SetPolicy(pid, status)) {
358
0
          return false;
359
0
        }
360
0
      }
361
0
    } else {
362
0
      if (!mf->SetPolicy(pid, cmPolicies::NEW)) {
363
0
        return false;
364
0
      }
365
0
    }
366
0
  }
367
368
  // Make sure the project does not use any ancient policies.
369
0
  if (!ancientPolicies.empty()) {
370
0
    DiagnoseAncientPolicies(ancientPolicies, majorVer, minorVer, patchVer, mf);
371
0
    cmSystemTools::SetFatalErrorOccurred();
372
0
    return false;
373
0
  }
374
375
0
  return true;
376
0
}
377
378
bool cmPolicies::GetPolicyID(char const* id, cmPolicies::PolicyID& pid)
379
0
{
380
0
  return stringToId(id, pid);
381
0
}
382
383
//! return a warning string for a given policy
384
std::string cmPolicies::GetPolicyWarning(cmPolicies::PolicyID id)
385
0
{
386
0
  return cmStrCat("Policy ", idToString(id),
387
0
                  " is not set: ", idToShortDescription(id),
388
0
                  "  "
389
0
                  "Run \"cmake --help-policy ",
390
0
                  idToString(id),
391
0
                  "\" for "
392
0
                  "policy details.  "
393
0
                  "Use the cmake_policy command to set the policy "
394
0
                  "and suppress this warning.");
395
0
}
396
397
std::string cmPolicies::GetPolicyDeprecatedWarning(cmPolicies::PolicyID id)
398
0
{
399
0
  return cmStrCat(
400
0
    "The OLD behavior for policy ", idToString(id),
401
0
    " "
402
0
    "will be removed from a future version of CMake.\n"
403
0
    "The cmake-policies(7) manual explains that the OLD behaviors of all "
404
0
    "policies are deprecated and that a policy should be set to OLD only "
405
0
    "under specific short-term circumstances.  Projects should be ported "
406
0
    "to the NEW behavior and not rely on setting a policy to OLD.");
407
0
}
408
409
bool cmPolicies::IsRemoved(cmPolicies::PolicyID id)
410
0
{
411
0
  return idToStatus(id) == cmPolicies::NEW;
412
0
}
413
414
std::string cmPolicies::GetRemovedPolicyError(cmPolicies::PolicyID id)
415
0
{
416
0
  std::string pid = idToString(id);
417
0
  return cmStrCat(
418
0
    "Policy ", pid,
419
0
    " may not be set to OLD behavior because this "
420
0
    "version of CMake no longer supports it.  "
421
0
    "The policy was introduced in CMake version ",
422
0
    idToVersion(id),
423
0
    ", and use of NEW behavior is now required."
424
0
    "\n"
425
0
    "Please either update your CMakeLists.txt files to conform to "
426
0
    "the new behavior or use an older version of CMake that still "
427
0
    "supports the old behavior.  Run cmake --help-policy ",
428
0
    pid, " for more information.");
429
0
}
430
431
cmPolicies::PolicyStatus cmPolicies::PolicyMap::Get(
432
  cmPolicies::PolicyID id) const
433
0
{
434
0
  PolicyStatus status = cmPolicies::WARN;
435
436
0
  if (this->Status[(POLICY_STATUS_COUNT * id) + OLD]) {
437
0
    status = cmPolicies::OLD;
438
0
  } else if (this->Status[(POLICY_STATUS_COUNT * id) + NEW]) {
439
0
    status = cmPolicies::NEW;
440
0
  }
441
0
  return status;
442
0
}
443
444
void cmPolicies::PolicyMap::Set(cmPolicies::PolicyID id,
445
                                cmPolicies::PolicyStatus status)
446
0
{
447
0
  this->Status[(POLICY_STATUS_COUNT * id) + OLD] = (status == OLD);
448
0
  this->Status[(POLICY_STATUS_COUNT * id) + WARN] = (status == WARN);
449
0
  this->Status[(POLICY_STATUS_COUNT * id) + NEW] = (status == NEW);
450
0
}
451
452
bool cmPolicies::PolicyMap::IsDefined(cmPolicies::PolicyID id) const
453
0
{
454
0
  return this->Status[(POLICY_STATUS_COUNT * id) + OLD] ||
455
0
    this->Status[(POLICY_STATUS_COUNT * id) + WARN] ||
456
0
    this->Status[(POLICY_STATUS_COUNT * id) + NEW];
457
0
}
458
459
bool cmPolicies::PolicyMap::IsEmpty() const
460
0
{
461
0
  return this->Status.none();
462
0
}