Coverage Report

Created: 2026-04-29 07:01

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