cmCacheManager.cxx 22.6 KB
Newer Older
1
2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3
#include "cmCacheManager.h"
Brad King's avatar
Brad King committed
4

5
6
#include "cmsys/FStream.hxx"
#include "cmsys/Glob.hxx"
7
8
9
10
#include <algorithm>
#include <sstream>
#include <stdio.h>
#include <string.h>
11
#include <string>
12

13
#include "cmGeneratedFileStream.h"
14
#include "cmMessageType.h"
15
#include "cmMessenger.h"
16
17
18
19
#include "cmState.h"
#include "cmSystemTools.h"
#include "cmVersion.h"

20
cmCacheManager::cmCacheManager()
21
22
23
24
25
{
  this->CacheMajorVersion = 0;
  this->CacheMinorVersion = 0;
}

26
void cmCacheManager::CleanCMakeFiles(const std::string& path)
27
28
{
  std::string glob = path;
29
  glob += "/CMakeFiles";
30
  glob += "/*.cmake";
31
  cmsys::Glob globIt;
32
33
  globIt.FindFiles(glob);
  std::vector<std::string> files = globIt.GetFiles();
34
  std::for_each(files.begin(), files.end(), cmSystemTools::RemoveFile);
35
36
}

37
bool cmCacheManager::LoadCache(const std::string& path, bool internal,
38
39
                               std::set<std::string>& excludes,
                               std::set<std::string>& includes)
40
41
{
  std::string cacheFile = path;
42
  cacheFile += "/CMakeCache.txt";
43
  // clear the old cache, if we are reading in internal values
44
  if (internal) {
Ken Martin's avatar
Ken Martin committed
45
    this->Cache.clear();
46
  }
47
  if (!cmSystemTools::FileExists(cacheFile)) {
48
49
    this->CleanCMakeFiles(path);
    return false;
50
  }
Andy Cedilnik's avatar
Andy Cedilnik committed
51

52
  cmsys::ifstream fin(cacheFile.c_str());
53
  if (!fin) {
54
    return false;
55
56
  }
  const char* realbuffer;
57
  std::string buffer;
58
  std::string entryKey;
59
  unsigned int lineno = 0;
60
  while (fin) {
61
    // Format is key:type=value
62
    std::string helpString;
63
    CacheEntry e;
64
    cmSystemTools::GetLineFromStream(fin, buffer);
65
    lineno++;
66
    realbuffer = buffer.c_str();
67
68
69
    while (*realbuffer != '0' &&
           (*realbuffer == ' ' || *realbuffer == '\t' || *realbuffer == '\r' ||
            *realbuffer == '\n')) {
70
      if (*realbuffer == '\n') {
71
        lineno++;
72
      }
73
      realbuffer++;
74
    }
Bill Hoffman's avatar
Bill Hoffman committed
75
    // skip blank lines and comment lines
76
    if (realbuffer[0] == '#' || realbuffer[0] == 0) {
Bill Hoffman's avatar
Bill Hoffman committed
77
      continue;
78
79
80
    }
    while (realbuffer[0] == '/' && realbuffer[1] == '/') {
      if ((realbuffer[2] == '\\') && (realbuffer[3] == 'n')) {
81
82
        helpString += "\n";
        helpString += &realbuffer[4];
83
      } else {
84
        helpString += &realbuffer[2];
85
      }
86
      cmSystemTools::GetLineFromStream(fin, buffer);
87
      lineno++;
88
      realbuffer = buffer.c_str();
89
      if (!fin) {
Bill Hoffman's avatar
Bill Hoffman committed
90
91
        continue;
      }
92
    }
93
    e.SetProperty("HELPSTRING", helpString.c_str());
94
95
    if (cmState::ParseCacheEntry(realbuffer, entryKey, e.Value, e.Type)) {
      if (excludes.find(entryKey) == excludes.end()) {
Andy Cedilnik's avatar
Andy Cedilnik committed
96
97
98
99
        // Load internal values if internal is set.
        // If the entry is not internal to the cache being loaded
        // or if it is in the list of internal entries to be
        // imported, load it.
100
        if (internal || (e.Type != cmStateEnums::INTERNAL) ||
101
            (includes.find(entryKey) != includes.end())) {
Andy Cedilnik's avatar
Andy Cedilnik committed
102
103
104
          // If we are loading the cache from another project,
          // make all loaded entries internal so that it is
          // not visible in the gui
105
          if (!internal) {
106
            e.Type = cmStateEnums::INTERNAL;
107
108
109
            helpString = "DO NOT EDIT, ";
            helpString += entryKey;
            helpString += " loaded from external file.  "
110
                          "To change this value edit this file: ";
111
            helpString += path;
112
            helpString += "/CMakeCache.txt";
113
            e.SetProperty("HELPSTRING", helpString.c_str());
114
115
          }
          if (!this->ReadPropertyEntry(entryKey, e)) {
Ken Martin's avatar
Ken Martin committed
116
117
            e.Initialized = true;
            this->Cache[entryKey] = e;
Andy Cedilnik's avatar
Andy Cedilnik committed
118
119
          }
        }
120
      }
121
    } else {
122
123
124
125
      std::ostringstream error;
      error << "Parse error in cache file " << cacheFile;
      error << " on line " << lineno << ". Offending entry: " << realbuffer;
      cmSystemTools::Error(error.str().c_str());
126
    }
127
  }
128
129
  this->CacheMajorVersion = 0;
  this->CacheMinorVersion = 0;
130
  if (const std::string* cmajor =
131
132
        this->GetInitializedCacheValue("CMAKE_CACHE_MAJOR_VERSION")) {
    unsigned int v = 0;
133
    if (sscanf(cmajor->c_str(), "%u", &v) == 1) {
134
      this->CacheMajorVersion = v;
135
    }
136
    if (const std::string* cminor =
137
          this->GetInitializedCacheValue("CMAKE_CACHE_MINOR_VERSION")) {
138
      if (sscanf(cminor->c_str(), "%u", &v) == 1) {
139
140
141
        this->CacheMinorVersion = v;
      }
    }
142
  } else {
143
144
    // CMake version not found in the list file.
    // Set as version 0.0
145
146
    this->AddCacheEntry("CMAKE_CACHE_MINOR_VERSION", "0",
                        "Minor version of cmake used to create the "
147
                        "current loaded cache",
148
                        cmStateEnums::INTERNAL);
149
150
    this->AddCacheEntry("CMAKE_CACHE_MAJOR_VERSION", "0",
                        "Major version of cmake used to create the "
151
                        "current loaded cache",
152
                        cmStateEnums::INTERNAL);
153
  }
154
155
  // check to make sure the cache directory has not
  // been moved
156
157
  const std::string* oldDir =
    this->GetInitializedCacheValue("CMAKE_CACHEFILE_DIR");
158
  if (internal && oldDir) {
159
    std::string currentcwd = path;
160
    std::string oldcwd = *oldDir;
161
    cmSystemTools::ConvertToUnixSlashes(currentcwd);
162
163
    currentcwd += "/CMakeCache.txt";
    oldcwd += "/CMakeCache.txt";
164
    if (!cmSystemTools::SameFile(oldcwd, currentcwd)) {
165
166
      const std::string* dir =
        this->GetInitializedCacheValue("CMAKE_CACHEFILE_DIR");
167
168
      std::ostringstream message;
      message << "The current CMakeCache.txt directory " << currentcwd
169
              << " is different than the directory " << (dir ? *dir : "")
170
171
172
173
              << " where CMakeCache.txt was created. This may result "
                 "in binaries being created in the wrong place. If you "
                 "are not sure, reedit the CMakeCache.txt";
      cmSystemTools::Error(message.str().c_str());
174
    }
175
  }
176
  return true;
177
178
}

179
const char* cmCacheManager::PersistentProperties[] = { "ADVANCED", "MODIFIED",
Daniel Pfeifer's avatar
Daniel Pfeifer committed
180
                                                       "STRINGS", nullptr };
181
182
183
184
185

bool cmCacheManager::ReadPropertyEntry(std::string const& entryKey,
                                       CacheEntry& e)
{
  // All property entries are internal.
186
  if (e.Type != cmStateEnums::INTERNAL) {
187
    return false;
188
  }
189
190

  const char* end = entryKey.c_str() + entryKey.size();
191
  for (const char** p = cmCacheManager::PersistentProperties; *p; ++p) {
192
    std::string::size_type plen = strlen(*p) + 1;
193
194
    if (entryKey.size() > plen && *(end - plen) == '-' &&
        strcmp(end - plen + 1, *p) == 0) {
195
196
      std::string key = entryKey.substr(0, entryKey.size() - plen);
      cmCacheManager::CacheIterator it = this->GetCacheIterator(key.c_str());
197
      if (it.IsAtEnd()) {
198
199
        // Create an entry and store the property.
        CacheEntry& ne = this->Cache[key];
200
        ne.Type = cmStateEnums::UNINITIALIZED;
201
        ne.SetProperty(*p, e.Value.c_str());
202
      } else {
203
204
205
        // Store this property on its entry.
        it.SetProperty(*p, e.Value.c_str());
      }
206
      return true;
207
    }
208
  }
209
210
211
  return false;
}

212
213
void cmCacheManager::WritePropertyEntries(std::ostream& os, CacheIterator i,
                                          cmMessenger* messenger)
214
{
215
  for (const char** p = cmCacheManager::PersistentProperties; *p; ++p) {
216
    if (const char* value = i.GetProperty(*p)) {
217
218
219
220
221
222
223
224
      std::string helpstring = *p;
      helpstring += " property for variable: ";
      helpstring += i.GetName();
      cmCacheManager::OutputHelpString(os, helpstring);

      std::string key = i.GetName();
      key += "-";
      key += *p;
225
      cmCacheManager::OutputKey(os, key);
226
      os << ":INTERNAL=";
227
      cmCacheManager::OutputValue(os, value);
228
      os << "\n";
229
230
      cmCacheManager::OutputNewlineTruncationWarning(os, key, value,
                                                     messenger);
231
    }
232
  }
233
234
}

235
bool cmCacheManager::SaveCache(const std::string& path, cmMessenger* messenger)
236
237
{
  std::string cacheFile = path;
238
  cacheFile += "/CMakeCache.txt";
239
  cmGeneratedFileStream fout(cacheFile);
240
  fout.SetCopyIfDifferent(true);
241
  if (!fout) {
Andy Cedilnik's avatar
Andy Cedilnik committed
242
    cmSystemTools::Error("Unable to open cache file for save. ",
243
                         cacheFile.c_str());
244
    cmSystemTools::ReportLastSystemError("");
245
    return false;
246
  }
Andy Cedilnik's avatar
Andy Cedilnik committed
247
  // before writing the cache, update the version numbers
Andy Cedilnik's avatar
Andy Cedilnik committed
248
  // to the
249
250
251
  this->AddCacheEntry("CMAKE_CACHE_MAJOR_VERSION",
                      std::to_string(cmVersion::GetMajorVersion()).c_str(),
                      "Major version of cmake used to create the "
252
                      "current loaded cache",
253
                      cmStateEnums::INTERNAL);
254
255
256
  this->AddCacheEntry("CMAKE_CACHE_MINOR_VERSION",
                      std::to_string(cmVersion::GetMinorVersion()).c_str(),
                      "Minor version of cmake used to create the "
257
                      "current loaded cache",
258
                      cmStateEnums::INTERNAL);
259
260
  this->AddCacheEntry("CMAKE_CACHE_PATCH_VERSION",
                      std::to_string(cmVersion::GetPatchVersion()).c_str(),
261
                      "Patch version of cmake used to create the "
262
                      "current loaded cache",
263
                      cmStateEnums::INTERNAL);
264

265
266
267
  // Let us store the current working directory so that if somebody
  // Copies it, he will not be surprised
  std::string currentcwd = path;
268
  if (currentcwd[0] >= 'A' && currentcwd[0] <= 'Z' && currentcwd[1] == ':') {
269
270
271
    // Cast added to avoid compiler warning. Cast is ok because
    // value is guaranteed to fit in char by the above if...
    currentcwd[0] = static_cast<char>(currentcwd[0] - 'A' + 'a');
272
  }
273
  cmSystemTools::ConvertToUnixSlashes(currentcwd);
274
  this->AddCacheEntry("CMAKE_CACHEFILE_DIR", currentcwd.c_str(),
275
                      "This is the directory where this CMakeCache.txt"
276
                      " was created",
277
                      cmStateEnums::INTERNAL);
278

279
  /* clang-format off */
Bill Hoffman's avatar
Bill Hoffman committed
280
  fout << "# This is the CMakeCache file.\n"
281
282
283
       << "# For build in directory: " << currentcwd << "\n"
       << "# It was generated by CMake: "
       << cmSystemTools::GetCMakeCommand() << std::endl;
284
  /* clang-format on */
285

286
  /* clang-format off */
Andy Cedilnik's avatar
Andy Cedilnik committed
287
288
289
290
291
292
  fout << "# You can edit this file to change values found and used by cmake."
       << std::endl
       << "# If you do not want to change any of the values, simply exit the "
       "editor." << std::endl
       << "# If you do want to change a value, simply edit, save, and exit "
       "the editor." << std::endl
Bill Hoffman's avatar
Bill Hoffman committed
293
294
       << "# The syntax for the file is as follows:\n"
       << "# KEY:TYPE=VALUE\n"
Bill Hoffman's avatar
Bill Hoffman committed
295
       << "# KEY is the name of a variable in the cache.\n"
296
       << "# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT "
Andy Cedilnik's avatar
Andy Cedilnik committed
297
       "TYPE!." << std::endl
Bill Hoffman's avatar
Bill Hoffman committed
298
       << "# VALUE is the current value for the KEY.\n\n";
299
  /* clang-format on */
Bill Hoffman's avatar
Bill Hoffman committed
300

Geoffrey Cross's avatar
Geoffrey Cross committed
301
302
303
304
305
  fout << "########################\n";
  fout << "# EXTERNAL cache entries\n";
  fout << "########################\n";
  fout << "\n";

306
307
  for (auto const& i : this->Cache) {
    CacheEntry const& ce = i.second;
308
    cmStateEnums::CacheEntryType t = ce.Type;
309
    if (!ce.Initialized) {
310
      /*
Andy Cedilnik's avatar
Andy Cedilnik committed
311
        // This should be added in, but is not for now.
Andy Cedilnik's avatar
Andy Cedilnik committed
312
      cmSystemTools::Error("Cache entry \"", (*i).first.c_str(),
313
                           "\" is uninitialized");
314
      */
315
    } else if (t != cmStateEnums::INTERNAL) {
Bill Hoffman's avatar
Bill Hoffman committed
316
      // Format is key:type=value
317
      if (const char* help = ce.GetProperty("HELPSTRING")) {
318
        cmCacheManager::OutputHelpString(fout, help);
319
      } else {
320
        cmCacheManager::OutputHelpString(fout, "Missing description");
321
      }
322
      cmCacheManager::OutputKey(fout, i.first);
323
      fout << ":" << cmState::CacheEntryTypeToString(t) << "=";
324
      cmCacheManager::OutputValue(fout, ce.Value);
325
326
327
328
      fout << "\n";
      cmCacheManager::OutputNewlineTruncationWarning(fout, i.first, ce.Value,
                                                     messenger);
      fout << "\n";
Bill Hoffman's avatar
Bill Hoffman committed
329
    }
330
  }
Geoffrey Cross's avatar
Geoffrey Cross committed
331
332
333
334
335
336
337

  fout << "\n";
  fout << "########################\n";
  fout << "# INTERNAL cache entries\n";
  fout << "########################\n";
  fout << "\n";

338
339
340
  for (cmCacheManager::CacheIterator i = this->NewIterator(); !i.IsAtEnd();
       i.Next()) {
    if (!i.Initialized()) {
341
      continue;
342
    }
343

344
    cmStateEnums::CacheEntryType t = i.GetType();
345
    this->WritePropertyEntries(fout, i, messenger);
346
    if (t == cmStateEnums::INTERNAL) {
347
      // Format is key:type=value
348
      if (const char* help = i.GetProperty("HELPSTRING")) {
349
        cmCacheManager::OutputHelpString(fout, help);
350
      }
351
      cmCacheManager::OutputKey(fout, i.GetName());
352
      fout << ":" << cmState::CacheEntryTypeToString(t) << "=";
353
      cmCacheManager::OutputValue(fout, i.GetValue());
Andy Cedilnik's avatar
Andy Cedilnik committed
354
      fout << "\n";
355
356
      cmCacheManager::OutputNewlineTruncationWarning(fout, i.GetName(),
                                                     i.GetValue(), messenger);
357
    }
358
  }
359
  fout << "\n";
360
  fout.Close();
361
  std::string checkCacheFile = path;
362
  checkCacheFile += "/CMakeFiles";
363
  cmSystemTools::MakeDirectory(checkCacheFile);
364
  checkCacheFile += "/cmake.check_cache";
365
  cmsys::ofstream checkCache(checkCacheFile.c_str());
366
  if (!checkCache) {
Andy Cedilnik's avatar
Andy Cedilnik committed
367
    cmSystemTools::Error("Unable to open check cache file for write. ",
368
369
                         checkCacheFile.c_str());
    return false;
370
  }
Andy Cedilnik's avatar
Andy Cedilnik committed
371
  checkCache << "# This file is generated by cmake for dependency checking "
372
                "of the CMakeCache.txt file\n";
373
374
  return true;
}
375

376
bool cmCacheManager::DeleteCache(const std::string& path)
Ken Martin's avatar
Ken Martin committed
377
378
{
  std::string cacheFile = path;
379
380
  cmSystemTools::ConvertToUnixSlashes(cacheFile);
  std::string cmakeFiles = cacheFile;
Ken Martin's avatar
Ken Martin committed
381
  cacheFile += "/CMakeCache.txt";
382
  if (cmSystemTools::FileExists(cacheFile)) {
383
    cmSystemTools::RemoveFile(cacheFile);
384
385
    // now remove the files in the CMakeFiles directory
    // this cleans up language cache files
386
    cmakeFiles += "/CMakeFiles";
387
    if (cmSystemTools::FileIsDirectory(cmakeFiles)) {
388
      cmSystemTools::RemoveADirectory(cmakeFiles);
389
    }
390
  }
Ken Martin's avatar
Ken Martin committed
391
392
393
  return true;
}

394
395
396
void cmCacheManager::OutputKey(std::ostream& fout, std::string const& key)
{
  // support : in key name by double quoting
397
  const char* q =
398
    (key.find(':') != std::string::npos || key.find("//") == 0) ? "\"" : "";
399
400
401
402
  fout << q << key << q;
}

void cmCacheManager::OutputValue(std::ostream& fout, std::string const& value)
403
404
405
406
407
408
409
410
411
412
413
414
415
{
  // look for and truncate newlines
  std::string::size_type newline = value.find('\n');
  if (newline != std::string::npos) {
    std::string truncated = value.substr(0, newline);
    OutputValueNoNewlines(fout, truncated);
  } else {
    OutputValueNoNewlines(fout, value);
  }
}

void cmCacheManager::OutputValueNoNewlines(std::ostream& fout,
                                           std::string const& value)
416
417
{
  // if value has trailing space or tab, enclose it in single quotes
418
  if (!value.empty() && (value.back() == ' ' || value.back() == '\t')) {
419
    fout << '\'' << value << '\'';
420
  } else {
421
    fout << value;
422
  }
423
424
425
}

void cmCacheManager::OutputHelpString(std::ostream& fout,
Bill Hoffman's avatar
Bill Hoffman committed
426
                                      const std::string& helpString)
427
{
Bill Hoffman's avatar
Bill Hoffman committed
428
  std::string::size_type end = helpString.size();
429
  if (end == 0) {
Bill Hoffman's avatar
Bill Hoffman committed
430
    return;
431
  }
Bill Hoffman's avatar
Bill Hoffman committed
432
433
  std::string oneLine;
  std::string::size_type pos = 0;
434
435
436
  for (std::string::size_type i = 0; i <= end; i++) {
    if ((i == end) || (helpString[i] == '\n') ||
        ((i - pos >= 60) && (helpString[i] == ' '))) {
Alexander Neundorf's avatar
   
Alexander Neundorf committed
437
      fout << "//";
438
      if (helpString[pos] == '\n') {
Alexander Neundorf's avatar
   
Alexander Neundorf committed
439
440
        pos++;
        fout << "\\n";
441
      }
Alexander Neundorf's avatar
   
Alexander Neundorf committed
442
      oneLine = helpString.substr(pos, i - pos);
443
      fout << oneLine << "\n";
Alexander Neundorf's avatar
   
Alexander Neundorf committed
444
      pos = i;
Bill Hoffman's avatar
Bill Hoffman committed
445
    }
446
  }
447
448
}

449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
void cmCacheManager::OutputWarningComment(std::ostream& fout,
                                          std::string const& message,
                                          bool wrapSpaces)
{
  std::string::size_type end = message.size();
  std::string oneLine;
  std::string::size_type pos = 0;
  for (std::string::size_type i = 0; i <= end; i++) {
    if ((i == end) || (message[i] == '\n') ||
        ((i - pos >= 60) && (message[i] == ' ') && wrapSpaces)) {
      fout << "# ";
      if (message[pos] == '\n') {
        pos++;
        fout << "\\n";
      }
      oneLine = message.substr(pos, i - pos);
      fout << oneLine << "\n";
      pos = i;
    }
  }
}

void cmCacheManager::OutputNewlineTruncationWarning(std::ostream& fout,
                                                    std::string const& key,
                                                    std::string const& value,
                                                    cmMessenger* messenger)
{
  if (value.find('\n') != std::string::npos) {
    if (messenger) {
      std::string message = "Value of ";
      message += key;
      message += " contained a newline; truncating";
481
      messenger->IssueMessage(MessageType::WARNING, message);
482
483
484
485
486
487
488
489
490
491
492
    }

    std::string comment = "WARNING: Value of ";
    comment += key;
    comment += " contained a newline and was truncated. Original value:";

    OutputWarningComment(fout, comment, true);
    OutputWarningComment(fout, value, false);
  }
}

493
void cmCacheManager::RemoveCacheEntry(const std::string& key)
494
{
Ken Martin's avatar
Ken Martin committed
495
  CacheEntryMap::iterator i = this->Cache.find(key);
496
  if (i != this->Cache.end()) {
Ken Martin's avatar
Ken Martin committed
497
    this->Cache.erase(i);
498
  }
499
500
}

501
502
cmCacheManager::CacheEntry* cmCacheManager::GetCacheEntry(
  const std::string& key)
503
{
Ken Martin's avatar
Ken Martin committed
504
  CacheEntryMap::iterator i = this->Cache.find(key);
505
  if (i != this->Cache.end()) {
506
    return &i->second;
507
  }
Daniel Pfeifer's avatar
Daniel Pfeifer committed
508
  return nullptr;
509
510
}

511
cmCacheManager::CacheIterator cmCacheManager::GetCacheIterator(const char* key)
512
513
514
515
{
  return CacheIterator(*this, key);
}

516
const std::string* cmCacheManager::GetInitializedCacheValue(
517
  const std::string& key) const
518
{
Ken Martin's avatar
Ken Martin committed
519
  CacheEntryMap::const_iterator i = this->Cache.find(key);
520
  if (i != this->Cache.end() && i->second.Initialized) {
521
    return &i->second.Value;
522
  }
Daniel Pfeifer's avatar
Daniel Pfeifer committed
523
  return nullptr;
524
}
Bill Hoffman's avatar
Bill Hoffman committed
525

526
void cmCacheManager::PrintCache(std::ostream& out) const
Bill Hoffman's avatar
Bill Hoffman committed
527
528
529
{
  out << "=================================================" << std::endl;
  out << "CMakeCache Contents:" << std::endl;
530
531
532
  for (auto const& i : this->Cache) {
    if (i.second.Type != cmStateEnums::INTERNAL) {
      out << i.first << " = " << i.second.Value << std::endl;
Bill Hoffman's avatar
Bill Hoffman committed
533
    }
534
  }
Bill Hoffman's avatar
Bill Hoffman committed
535
  out << "\n\n";
536
537
  out << "To change values in the CMakeCache, " << std::endl
      << "edit CMakeCache.txt in your output directory.\n";
Bill Hoffman's avatar
Bill Hoffman committed
538
539
540
  out << "=================================================" << std::endl;
}

541
void cmCacheManager::AddCacheEntry(const std::string& key, const char* value,
Bill Hoffman's avatar
Bill Hoffman committed
542
                                   const char* helpString,
543
                                   cmStateEnums::CacheEntryType type)
Bill Hoffman's avatar
Bill Hoffman committed
544
{
Ken Martin's avatar
Ken Martin committed
545
  CacheEntry& e = this->Cache[key];
546
  if (value) {
Ken Martin's avatar
Ken Martin committed
547
548
    e.Value = value;
    e.Initialized = true;
549
  } else {
550
    e.Value.clear();
551
  }
Ken Martin's avatar
Ken Martin committed
552
  e.Type = type;
553
  // make sure we only use unix style paths
554
  if (type == cmStateEnums::FILEPATH || type == cmStateEnums::PATH) {
555
    if (e.Value.find(';') != std::string::npos) {
556
557
558
      std::vector<std::string> paths;
      cmSystemTools::ExpandListArgument(e.Value, paths);
      const char* sep = "";
Bill Hoffman's avatar
Bill Hoffman committed
559
      e.Value = "";
560
561
      for (std::string& i : paths) {
        cmSystemTools::ConvertToUnixSlashes(i);
562
        e.Value += sep;
563
        e.Value += i;
564
565
        sep = ";";
      }
566
    } else {
567
      cmSystemTools::ConvertToUnixSlashes(e.Value);
568
    }
569
  }
570
571
  e.SetProperty("HELPSTRING",
                helpString
572
573
                  ? helpString
                  : "(This variable does not exist and should not be used)");
Bill Hoffman's avatar
Bill Hoffman committed
574
575
}

576
bool cmCacheManager::CacheIterator::IsAtEnd() const
577
{
Ken Martin's avatar
Ken Martin committed
578
  return this->Position == this->Container.Cache.end();
579
580
}

Andy Cedilnik's avatar
Andy Cedilnik committed
581
void cmCacheManager::CacheIterator::Begin()
582
{
583
  this->Position = this->Container.Cache.begin();
584
585
}

586
bool cmCacheManager::CacheIterator::Find(const std::string& key)
587
{
Ken Martin's avatar
Ken Martin committed
588
  this->Position = this->Container.Cache.find(key);
589
  return !this->IsAtEnd();
590
591
}

Andy Cedilnik's avatar
Andy Cedilnik committed
592
void cmCacheManager::CacheIterator::Next()
593
{
594
  if (!this->IsAtEnd()) {
595
    ++this->Position;
596
  }
597
598
}

599
600
601
602
603
std::vector<std::string> cmCacheManager::CacheIterator::GetPropertyList() const
{
  return this->GetEntry().GetPropertyList();
}

604
605
void cmCacheManager::CacheIterator::SetValue(const char* value)
{
606
  if (this->IsAtEnd()) {
607
    return;
608
  }
609
  CacheEntry* entry = &this->GetEntry();
610
  if (value) {
Ken Martin's avatar
Ken Martin committed
611
612
    entry->Value = value;
    entry->Initialized = true;
613
  } else {
614
    entry->Value.clear();
615
  }
616
617
}

618
bool cmCacheManager::CacheIterator::GetValueAsBool() const
619
{
620
  return cmSystemTools::IsOn(this->GetEntry().Value);
621
}
622

623
624
625
626
627
std::vector<std::string> cmCacheManager::CacheEntry::GetPropertyList() const
{
  return this->Properties.GetPropertyList();
}

628
629
const char* cmCacheManager::CacheEntry::GetProperty(
  const std::string& prop) const
630
{
631
  if (prop == "TYPE") {
632
    return cmState::CacheEntryTypeToString(this->Type);
633
634
  }
  if (prop == "VALUE") {
635
    return this->Value.c_str();
636
  }
637
  return this->Properties.GetPropertyValue(prop);
638
}
Ken Martin's avatar
Ken Martin committed
639

640
void cmCacheManager::CacheEntry::SetProperty(const std::string& prop,
641
                                             const char* value)
Ken Martin's avatar
Ken Martin committed
642
{
643
644
645
646
647
  if (prop == "TYPE") {
    this->Type = cmState::StringToCacheEntryType(value ? value : "STRING");
  } else if (prop == "VALUE") {
    this->Value = value ? value : "";
  } else {
648
    this->Properties.SetProperty(prop, value);
649
  }
650
651
}

652
void cmCacheManager::CacheEntry::AppendProperty(const std::string& prop,
653
654
                                                const char* value,
                                                bool asString)
Ken Martin's avatar
Ken Martin committed
655
{
656
657
658
659
660
  if (prop == "TYPE") {
    this->Type = cmState::StringToCacheEntryType(value ? value : "STRING");
  } else if (prop == "VALUE") {
    if (value) {
      if (!this->Value.empty() && *value && !asString) {
661
662
        this->Value += ";";
      }
663
      this->Value += value;
664
    }
665
  } else {
666
    this->Properties.AppendProperty(prop, value, asString);
667
  }
Ken Martin's avatar
Ken Martin committed
668
669
}

670
const char* cmCacheManager::CacheIterator::GetProperty(
671
  const std::string& prop) const
Ken Martin's avatar
Ken Martin committed
672
{
673
  if (!this->IsAtEnd()) {
674
    return this->GetEntry().GetProperty(prop);
675
  }
Daniel Pfeifer's avatar
Daniel Pfeifer committed
676
  return nullptr;
677
}
678

679
680
void cmCacheManager::CacheIterator::SetProperty(const std::string& p,
                                                const char* v)
681
{
682
  if (!this->IsAtEnd()) {
Brad King's avatar
Brad King committed
683
    this->GetEntry().SetProperty(p, v);
684
  }
Ken Martin's avatar
Ken Martin committed
685
}
686

687
void cmCacheManager::CacheIterator::AppendProperty(const std::string& p,
688
689
                                                   const char* v,
                                                   bool asString)
690
{
691
  if (!this->IsAtEnd()) {
692
    this->GetEntry().AppendProperty(p, v, asString);
693
  }
694
}
695

696
bool cmCacheManager::CacheIterator::GetPropertyAsBool(
697
  const std::string& prop) const
698
{
699
  if (const char* value = this->GetProperty(prop)) {
700
    return cmSystemTools::IsOn(value);
701
  }
702
703
704
  return false;
}

705
void cmCacheManager::CacheIterator::SetProperty(const std::string& p, bool v)
706
707
708
709
{
  this->SetProperty(p, v ? "ON" : "OFF");
}

710
bool cmCacheManager::CacheIterator::PropertyExists(
711
  const std::string& prop) const
712
{
Daniel Pfeifer's avatar
Daniel Pfeifer committed
713
  return this->GetProperty(prop) != nullptr;
714
}