Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmLoadCacheCommand.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 "cmLoadCacheCommand.h"
4
5
#include <set>
6
7
#include "cmsys/FStream.hxx"
8
9
#include "cmExecutionStatus.h"
10
#include "cmMakefile.h"
11
#include "cmState.h"
12
#include "cmStateTypes.h"
13
#include "cmSystemTools.h"
14
#include "cmake.h"
15
16
static bool ReadWithPrefix(std::vector<std::string> const& args,
17
                           cmExecutionStatus& status);
18
19
static void CheckLine(cmMakefile& mf, std::string const& prefix,
20
                      std::set<std::string> const& variablesToRead,
21
                      char const* line);
22
23
bool cmLoadCacheCommand(std::vector<std::string> const& args,
24
                        cmExecutionStatus& status)
25
0
{
26
0
  if (args.empty()) {
27
0
    status.SetError("called with wrong number of arguments.");
28
0
    return false;
29
0
  }
30
31
0
  if (args.size() >= 2 && args[1] == "READ_WITH_PREFIX") {
32
0
    return ReadWithPrefix(args, status);
33
0
  }
34
35
0
  cmState::Role const role =
36
0
    status.GetMakefile().GetCMakeInstance()->GetState()->GetRole();
37
0
  if (role != cmState::Role::Project) {
38
0
    status.SetError(
39
0
      "Only load_cache(READ_WITH_PREFIX) may be used in script mode");
40
0
    return false;
41
0
  }
42
43
  // Cache entries to be excluded from the import list.
44
  // If this set is empty, all cache entries are brought in
45
  // and they can not be overridden.
46
0
  bool excludeFiles = false;
47
0
  std::set<std::string> excludes;
48
49
0
  for (std::string const& arg : args) {
50
0
    if (excludeFiles) {
51
0
      excludes.insert(arg);
52
0
    }
53
0
    if (arg == "EXCLUDE") {
54
0
      excludeFiles = true;
55
0
    }
56
0
    if (excludeFiles && (arg == "INCLUDE_INTERNALS")) {
57
0
      break;
58
0
    }
59
0
  }
60
61
  // Internal cache entries to be imported.
62
  // If this set is empty, no internal cache entries are
63
  // brought in.
64
0
  bool includeFiles = false;
65
0
  std::set<std::string> includes;
66
67
0
  for (std::string const& arg : args) {
68
0
    if (includeFiles) {
69
0
      includes.insert(arg);
70
0
    }
71
0
    if (arg == "INCLUDE_INTERNALS") {
72
0
      includeFiles = true;
73
0
    }
74
0
    if (includeFiles && (arg == "EXCLUDE")) {
75
0
      break;
76
0
    }
77
0
  }
78
79
0
  cmMakefile& mf = status.GetMakefile();
80
81
  // Loop over each build directory listed in the arguments.  Each
82
  // directory has a cache file.
83
0
  for (std::string const& arg : args) {
84
0
    if ((arg == "EXCLUDE") || (arg == "INCLUDE_INTERNALS")) {
85
0
      break;
86
0
    }
87
0
    mf.GetCMakeInstance()->LoadCache(arg, false, excludes, includes);
88
0
  }
89
90
0
  return true;
91
0
}
92
93
static bool ReadWithPrefix(std::vector<std::string> const& args,
94
                           cmExecutionStatus& status)
95
0
{
96
  // Make sure we have a prefix.
97
0
  if (args.size() < 3) {
98
0
    status.SetError("READ_WITH_PREFIX form must specify a prefix.");
99
0
    return false;
100
0
  }
101
102
  // Make sure the cache file exists.
103
0
  std::string cacheFile = args[0] + "/CMakeCache.txt";
104
0
  if (!cmSystemTools::FileExists(cacheFile)) {
105
0
    std::string e = "Cannot load cache file from " + cacheFile;
106
0
    status.SetError(e);
107
0
    return false;
108
0
  }
109
110
  // Prepare the table of variables to read.
111
0
  std::string const& prefix = args[2];
112
0
  std::set<std::string> const variablesToRead(args.begin() + 3, args.end());
113
114
  // Read the cache file.
115
0
  cmsys::ifstream fin(cacheFile.c_str());
116
117
0
  cmMakefile& mf = status.GetMakefile();
118
119
  // This is a big hack read loop to overcome a buggy ifstream
120
  // implementation on HP-UX.  This should work on all platforms even
121
  // for small buffer sizes.
122
0
  int const bufferSize = 4096;
123
0
  char buffer[bufferSize];
124
0
  std::string line;
125
0
  while (fin) {
126
    // Read a block of the file.
127
0
    fin.read(buffer, bufferSize);
128
0
    if (fin.gcount()) {
129
      // Parse for newlines directly.
130
0
      char const* i = buffer;
131
0
      char const* end = buffer + fin.gcount();
132
0
      while (i != end) {
133
0
        char const* begin = i;
134
0
        while (i != end && *i != '\n') {
135
0
          ++i;
136
0
        }
137
0
        if (i == begin || *(i - 1) != '\r') {
138
          // Include this portion of the line.
139
0
          line += std::string(begin, i - begin);
140
0
        } else {
141
          // Include this portion of the line.
142
          // Don't include the \r in a \r\n pair.
143
0
          line += std::string(begin, i - 1 - begin);
144
0
        }
145
0
        if (i != end) {
146
          // Completed a line.
147
0
          CheckLine(mf, prefix, variablesToRead, line.c_str());
148
0
          line.clear();
149
150
          // Skip the newline character.
151
0
          ++i;
152
0
        }
153
0
      }
154
0
    }
155
0
  }
156
0
  if (!line.empty()) {
157
    // Partial last line.
158
0
    CheckLine(mf, prefix, variablesToRead, line.c_str());
159
0
  }
160
161
0
  return true;
162
0
}
163
164
static void CheckLine(cmMakefile& mf, std::string const& prefix,
165
                      std::set<std::string> const& variablesToRead,
166
                      char const* line)
167
0
{
168
  // Check one line of the cache file.
169
0
  std::string var;
170
0
  std::string value;
171
0
  cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
172
0
  if (cmake::ParseCacheEntry(line, var, value, type)) {
173
    // Found a real entry.  See if this one was requested.
174
0
    if (variablesToRead.find(var) != variablesToRead.end()) {
175
      // This was requested.  Set this variable locally with the given
176
      // prefix.
177
0
      var = prefix + var;
178
0
      if (!value.empty()) {
179
0
        mf.AddDefinition(var, value);
180
0
      } else {
181
0
        mf.RemoveDefinition(var);
182
0
      }
183
0
    }
184
0
  }
185
0
}