mirror of
https://github.com/Kitware/CMake.git
synced 2025-05-09 14:57:08 +08:00

In certain cases, rebuilds with CMake using the CONFIGURE_DEPENDS flag with GLOB_RECURSE could result in a reconfigure loop due to CMP0009 not being propogated to the generated VerifyGlobs.cmake script. During the inital configuration phase, RecurseThroughSymlinksOn() is called for recursive glob operations either by having the CMP0009 status not set to NEW or by explicitly providing the FOLLOW_SYMLINKS flag. At the end when the VerifyGlobs script is created, the FOLLOW_SYMLINKS flag is written according to the final resolved form through a call to GetRecurseThroughSymlinks(). Thus, setting CMP0009 to NEW in the generated file is safe and allows correct behavior whether or not the end user sets the policy status to NEW or OLD.
182 lines
5.4 KiB
C++
182 lines
5.4 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
#include "cmGlobVerificationManager.h"
|
|
|
|
#include <sstream>
|
|
|
|
#include "cmsys/FStream.hxx"
|
|
|
|
#include "cmGeneratedFileStream.h"
|
|
#include "cmListFileCache.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cmVersion.h"
|
|
|
|
bool cmGlobVerificationManager::SaveVerificationScript(const std::string& path)
|
|
{
|
|
if (this->Cache.empty()) {
|
|
return true;
|
|
}
|
|
|
|
std::string scriptFile = cmStrCat(path, "/CMakeFiles");
|
|
std::string stampFile = scriptFile;
|
|
cmSystemTools::MakeDirectory(scriptFile);
|
|
scriptFile += "/VerifyGlobs.cmake";
|
|
stampFile += "/cmake.verify_globs";
|
|
cmGeneratedFileStream verifyScriptFile(scriptFile);
|
|
verifyScriptFile.SetCopyIfDifferent(true);
|
|
if (!verifyScriptFile) {
|
|
cmSystemTools::Error("Unable to open verification script file for save. " +
|
|
scriptFile);
|
|
cmSystemTools::ReportLastSystemError("");
|
|
return false;
|
|
}
|
|
|
|
verifyScriptFile << std::boolalpha;
|
|
verifyScriptFile << "# CMAKE generated file: DO NOT EDIT!\n"
|
|
<< "# Generated by CMake Version "
|
|
<< cmVersion::GetMajorVersion() << "."
|
|
<< cmVersion::GetMinorVersion() << "\n";
|
|
|
|
verifyScriptFile << "cmake_policy(SET CMP0009 NEW)\n";
|
|
|
|
for (auto const& i : this->Cache) {
|
|
CacheEntryKey k = std::get<0>(i);
|
|
CacheEntryValue v = std::get<1>(i);
|
|
|
|
if (!v.Initialized) {
|
|
continue;
|
|
}
|
|
|
|
verifyScriptFile << "\n";
|
|
|
|
for (auto const& bt : v.Backtraces) {
|
|
verifyScriptFile << "# " << std::get<0>(bt);
|
|
std::get<1>(bt).PrintTitle(verifyScriptFile);
|
|
verifyScriptFile << "\n";
|
|
}
|
|
|
|
k.PrintGlobCommand(verifyScriptFile, "NEW_GLOB");
|
|
verifyScriptFile << "\n";
|
|
|
|
verifyScriptFile << "set(OLD_GLOB\n";
|
|
for (const std::string& file : v.Files) {
|
|
verifyScriptFile << " \"" << file << "\"\n";
|
|
}
|
|
verifyScriptFile << " )\n";
|
|
|
|
verifyScriptFile << "if(NOT \"${NEW_GLOB}\" STREQUAL \"${OLD_GLOB}\")\n"
|
|
<< " message(\"-- GLOB mismatch!\")\n"
|
|
<< " file(TOUCH_NOCREATE \"" << stampFile << "\")\n"
|
|
<< "endif()\n";
|
|
}
|
|
verifyScriptFile.Close();
|
|
|
|
cmsys::ofstream verifyStampFile(stampFile.c_str());
|
|
if (!verifyStampFile) {
|
|
cmSystemTools::Error("Unable to open verification stamp file for write. " +
|
|
stampFile);
|
|
return false;
|
|
}
|
|
verifyStampFile << "# This file is generated by CMake for checking of the "
|
|
"VerifyGlobs.cmake file\n";
|
|
this->VerifyScript = scriptFile;
|
|
this->VerifyStamp = stampFile;
|
|
return true;
|
|
}
|
|
|
|
bool cmGlobVerificationManager::DoWriteVerifyTarget() const
|
|
{
|
|
return !this->VerifyScript.empty() && !this->VerifyStamp.empty();
|
|
}
|
|
|
|
bool cmGlobVerificationManager::CacheEntryKey::operator<(
|
|
const CacheEntryKey& r) const
|
|
{
|
|
if (this->Recurse < r.Recurse) {
|
|
return true;
|
|
}
|
|
if (this->Recurse > r.Recurse) {
|
|
return false;
|
|
}
|
|
if (this->ListDirectories < r.ListDirectories) {
|
|
return true;
|
|
}
|
|
if (this->ListDirectories > r.ListDirectories) {
|
|
return false;
|
|
}
|
|
if (this->FollowSymlinks < r.FollowSymlinks) {
|
|
return true;
|
|
}
|
|
if (this->FollowSymlinks > r.FollowSymlinks) {
|
|
return false;
|
|
}
|
|
if (this->Relative < r.Relative) {
|
|
return true;
|
|
}
|
|
if (this->Relative > r.Relative) {
|
|
return false;
|
|
}
|
|
if (this->Expression < r.Expression) {
|
|
return true;
|
|
}
|
|
if (this->Expression > r.Expression) {
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void cmGlobVerificationManager::CacheEntryKey::PrintGlobCommand(
|
|
std::ostream& out, const std::string& cmdVar)
|
|
{
|
|
out << "file(GLOB" << (this->Recurse ? "_RECURSE " : " ");
|
|
out << cmdVar << " ";
|
|
if (this->Recurse && this->FollowSymlinks) {
|
|
out << "FOLLOW_SYMLINKS ";
|
|
}
|
|
out << "LIST_DIRECTORIES " << this->ListDirectories << " ";
|
|
if (!this->Relative.empty()) {
|
|
out << "RELATIVE \"" << this->Relative << "\" ";
|
|
}
|
|
out << "\"" << this->Expression << "\")";
|
|
}
|
|
|
|
void cmGlobVerificationManager::AddCacheEntry(
|
|
const bool recurse, const bool listDirectories, const bool followSymlinks,
|
|
const std::string& relative, const std::string& expression,
|
|
const std::vector<std::string>& files, const std::string& variable,
|
|
const cmListFileBacktrace& backtrace)
|
|
{
|
|
CacheEntryKey key = CacheEntryKey(recurse, listDirectories, followSymlinks,
|
|
relative, expression);
|
|
CacheEntryValue& value = this->Cache[key];
|
|
if (!value.Initialized) {
|
|
value.Files = files;
|
|
value.Initialized = true;
|
|
value.Backtraces.emplace_back(variable, backtrace);
|
|
} else if (value.Initialized && value.Files != files) {
|
|
std::ostringstream message;
|
|
message << std::boolalpha;
|
|
message << "The glob expression\n";
|
|
key.PrintGlobCommand(message, variable);
|
|
backtrace.PrintTitle(message);
|
|
message << "\nwas already present in the glob cache but the directory\n"
|
|
"contents have changed during the configuration run.\n";
|
|
message << "Matching glob expressions:";
|
|
for (auto const& bt : value.Backtraces) {
|
|
message << "\n " << std::get<0>(bt);
|
|
std::get<1>(bt).PrintTitle(message);
|
|
}
|
|
cmSystemTools::Error(message.str());
|
|
} else {
|
|
value.Backtraces.emplace_back(variable, backtrace);
|
|
}
|
|
}
|
|
|
|
void cmGlobVerificationManager::Reset()
|
|
{
|
|
this->Cache.clear();
|
|
this->VerifyScript.clear();
|
|
this->VerifyStamp.clear();
|
|
}
|