/* 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 #include "cmsys/FStream.hxx" #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmStateTypes.h" #include "cmSystemTools.h" #include "cmake.h" static bool ReadWithPrefix(std::vector const& args, cmExecutionStatus& status); static void CheckLine(cmMakefile& mf, std::string const& prefix, std::set const& variablesToRead, const char* line); bool cmLoadCacheCommand(std::vector 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 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 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 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 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 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); } } } }