|
|
|
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
|
|
#include "cmLoadCacheCommand.h"
|
|
|
|
|
|
|
|
#include <set>
|
|
|
|
|
|
|
|
#include "cmsys/FStream.hxx"
|
|
|
|
|
|
|
|
#include "cmExecutionStatus.h"
|
|
|
|
#include "cmMakefile.h"
|
|
|
|
#include "cmStateTypes.h"
|
|
|
|
#include "cmSystemTools.h"
|
|
|
|
#include "cmake.h"
|
|
|
|
|
|
|
|
static bool ReadWithPrefix(std::vector<std::string> const& args,
|
|
|
|
cmExecutionStatus& status);
|
|
|
|
|
|
|
|
static void CheckLine(cmMakefile& mf, std::string const& prefix,
|
|
|
|
std::set<std::string> const& variablesToRead,
|
|
|
|
const char* line);
|
|
|
|
|
|
|
|
bool cmLoadCacheCommand(std::vector<std::string> const& args,
|
|
|
|
cmExecutionStatus& status)
|
|
|
|
{
|
|
|
|
if (args.empty()) {
|
|
|
|
status.SetError("called with wrong number of arguments.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (args.size() >= 2 && args[1] == "READ_WITH_PREFIX") {
|
|
|
|
return ReadWithPrefix(args, status);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status.GetMakefile().GetCMakeInstance()->GetWorkingMode() ==
|
|
|
|
cmake::SCRIPT_MODE) {
|
|
|
|
status.SetError(
|
|
|
|
"Only load_cache(READ_WITH_PREFIX) may be used in script mode");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cache entries to be excluded from the import list.
|
|
|
|
// If this set is empty, all cache entries are brought in
|
|
|
|
// and they can not be overridden.
|
|
|
|
bool excludeFiles = false;
|
|
|
|
std::set<std::string> excludes;
|
|
|
|
|
|
|
|
for (std::string const& arg : args) {
|
|
|
|
if (excludeFiles) {
|
|
|
|
excludes.insert(arg);
|
|
|
|
}
|
|
|
|
if (arg == "EXCLUDE") {
|
|
|
|
excludeFiles = true;
|
|
|
|
}
|
|
|
|
if (excludeFiles && (arg == "INCLUDE_INTERNALS")) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Internal cache entries to be imported.
|
|
|
|
// If this set is empty, no internal cache entries are
|
|
|
|
// brought in.
|
|
|
|
bool includeFiles = false;
|
|
|
|
std::set<std::string> includes;
|
|
|
|
|
|
|
|
for (std::string const& arg : args) {
|
|
|
|
if (includeFiles) {
|
|
|
|
includes.insert(arg);
|
|
|
|
}
|
|
|
|
if (arg == "INCLUDE_INTERNALS") {
|
|
|
|
includeFiles = true;
|
|
|
|
}
|
|
|
|
if (includeFiles && (arg == "EXCLUDE")) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cmMakefile& mf = status.GetMakefile();
|
|
|
|
|
|
|
|
// Loop over each build directory listed in the arguments. Each
|
|
|
|
// directory has a cache file.
|
|
|
|
for (std::string const& arg : args) {
|
|
|
|
if ((arg == "EXCLUDE") || (arg == "INCLUDE_INTERNALS")) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
mf.GetCMakeInstance()->LoadCache(arg, false, excludes, includes);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool ReadWithPrefix(std::vector<std::string> const& args,
|
|
|
|
cmExecutionStatus& status)
|
|
|
|
{
|
|
|
|
// Make sure we have a prefix.
|
|
|
|
if (args.size() < 3) {
|
|
|
|
status.SetError("READ_WITH_PREFIX form must specify a prefix.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the cache file exists.
|
|
|
|
std::string cacheFile = args[0] + "/CMakeCache.txt";
|
|
|
|
if (!cmSystemTools::FileExists(cacheFile)) {
|
|
|
|
std::string e = "Cannot load cache file from " + cacheFile;
|
|
|
|
status.SetError(e);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prepare the table of variables to read.
|
|
|
|
std::string const& prefix = args[2];
|
|
|
|
std::set<std::string> const variablesToRead(args.begin() + 3, args.end());
|
|
|
|
|
|
|
|
// Read the cache file.
|
|
|
|
cmsys::ifstream fin(cacheFile.c_str());
|
|
|
|
|
|
|
|
cmMakefile& mf = status.GetMakefile();
|
|
|
|
|
|
|
|
// This is a big hack read loop to overcome a buggy ifstream
|
|
|
|
// implementation on HP-UX. This should work on all platforms even
|
|
|
|
// for small buffer sizes.
|
|
|
|
const int bufferSize = 4096;
|
|
|
|
char buffer[bufferSize];
|
|
|
|
std::string line;
|
|
|
|
while (fin) {
|
|
|
|
// Read a block of the file.
|
|
|
|
fin.read(buffer, bufferSize);
|
|
|
|
if (fin.gcount()) {
|
|
|
|
// Parse for newlines directly.
|
|
|
|
const char* i = buffer;
|
|
|
|
const char* end = buffer + fin.gcount();
|
|
|
|
while (i != end) {
|
|
|
|
const char* begin = i;
|
|
|
|
while (i != end && *i != '\n') {
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
if (i == begin || *(i - 1) != '\r') {
|
|
|
|
// Include this portion of the line.
|
|
|
|
line += std::string(begin, i - begin);
|
|
|
|
} else {
|
|
|
|
// Include this portion of the line.
|
|
|
|
// Don't include the \r in a \r\n pair.
|
|
|
|
line += std::string(begin, i - 1 - begin);
|
|
|
|
}
|
|
|
|
if (i != end) {
|
|
|
|
// Completed a line.
|
|
|
|
CheckLine(mf, prefix, variablesToRead, line.c_str());
|
|
|
|
line.clear();
|
|
|
|
|
|
|
|
// Skip the newline character.
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!line.empty()) {
|
|
|
|
// Partial last line.
|
|
|
|
CheckLine(mf, prefix, variablesToRead, line.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void CheckLine(cmMakefile& mf, std::string const& prefix,
|
|
|
|
std::set<std::string> const& variablesToRead,
|
|
|
|
const char* line)
|
|
|
|
{
|
|
|
|
// Check one line of the cache file.
|
|
|
|
std::string var;
|
|
|
|
std::string value;
|
|
|
|
cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
|
|
|
|
if (cmake::ParseCacheEntry(line, var, value, type)) {
|
|
|
|
// Found a real entry. See if this one was requested.
|
|
|
|
if (variablesToRead.find(var) != variablesToRead.end()) {
|
|
|
|
// This was requested. Set this variable locally with the given
|
|
|
|
// prefix.
|
|
|
|
var = prefix + var;
|
|
|
|
if (!value.empty()) {
|
|
|
|
mf.AddDefinition(var, value);
|
|
|
|
} else {
|
|
|
|
mf.RemoveDefinition(var);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|