Coverage Report

Created: 2026-03-12 06:35

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