ENH: Added build system integrity check to cmLocalUnixMakefileGenerator2. This now uses a special --check-build-system flag to cmake which replaces --check-rerun. Integrity of dependencies is also checked during generation.

This commit is contained in:
Brad King 2004-10-29 16:50:46 -04:00
parent 674349caed
commit 81bbae1fb2
4 changed files with 261 additions and 54 deletions

View File

@ -149,6 +149,19 @@ void cmLocalUnixMakefileGenerator2::GenerateCMakefile()
<< "# The corresponding makefile is:\n" << "# The corresponding makefile is:\n"
<< "SET(CMAKE_MAKEFILE_OUTPUTS\n" << "SET(CMAKE_MAKEFILE_OUTPUTS\n"
<< " \"" << makefileName.c_str() << "\"\n" << " \"" << makefileName.c_str() << "\"\n"
<< " )\n\n";
// Set the set of files to check for dependency integrity.
cmakefileStream
<< "# The set of files whose dependency integrity should be checked:\n"
<< "SET(CMAKE_DEPENDS_CHECK\n";
for(std::set<cmStdString>::const_iterator i = m_CheckDependFiles.begin();
i != m_CheckDependFiles.end(); ++i)
{
cmakefileStream
<< " \"" << i->c_str() << "\"\n";
}
cmakefileStream
<< " )\n"; << " )\n";
} }
@ -180,20 +193,11 @@ cmLocalUnixMakefileGenerator2
} }
} }
// If there is no dependencies file, create an empty one. // Generate the build-time dependencies file for this target.
std::string depFileName = dir; std::string depBase = dir;
depFileName += "/"; depBase += "/";
depFileName += target.GetName(); depBase += target.GetName();
depFileName += ".depends.make"; std::string depMakeFile = this->GenerateDependsMakeFile(depBase.c_str());
std::string depFileNameFull = this->ConvertToFullPath(depFileName);
if(!cmSystemTools::FileExists(depFileNameFull.c_str()))
{
std::ofstream depFileStream(depFileNameFull.c_str());
this->WriteDisclaimer(depFileStream);
depFileStream
<< "# Empty dependencies file for target " << target.GetName() << ".\n"
<< "# This may be replaced when dependencies are built.\n";
}
// Open the rule file. This should be copy-if-different because the // Open the rule file. This should be copy-if-different because the
// rules may depend on this file itself. // rules may depend on this file itself.
@ -218,7 +222,7 @@ cmLocalUnixMakefileGenerator2
ruleFileStream ruleFileStream
<< "# Include any dependencies generated for this rule.\n" << "# Include any dependencies generated for this rule.\n"
<< m_IncludeDirective << " " << m_IncludeDirective << " "
<< this->ConvertToOutputForExisting(depFileName.c_str()).c_str() << this->ConvertToOutputForExisting(depMakeFile.c_str()).c_str()
<< "\n\n"; << "\n\n";
// Include the rule file for each object. // Include the rule file for each object.
@ -243,7 +247,7 @@ cmLocalUnixMakefileGenerator2
// Write the dependency generation rule. // Write the dependency generation rule.
{ {
std::vector<std::string> depends; std::vector<std::string> depends;
std::vector<std::string> commands; std::vector<std::string> no_commands;
std::string depEcho = "Building dependencies for "; std::string depEcho = "Building dependencies for ";
depEcho += target.GetName(); depEcho += target.GetName();
depEcho += "..."; depEcho += "...";
@ -258,7 +262,7 @@ cmLocalUnixMakefileGenerator2
} }
depends.push_back(ruleFileName); depends.push_back(ruleFileName);
this->WriteMakeRule(ruleFileStream, 0, depEcho.c_str(), this->WriteMakeRule(ruleFileStream, 0, depEcho.c_str(),
depTarget.c_str(), depends, commands); depTarget.c_str(), depends, no_commands);
} }
// Write the build rule. // Write the build rule.
@ -303,23 +307,16 @@ cmLocalUnixMakefileGenerator2
// Get the full path name of the object file. // Get the full path name of the object file.
std::string obj = this->GetObjectFileName(target, source); std::string obj = this->GetObjectFileName(target, source);
// The object file should be checked for dependency integrity.
m_CheckDependFiles.insert(obj);
// Create the directory containing the object file. This may be a // Create the directory containing the object file. This may be a
// subdirectory under the target's directory. // subdirectory under the target's directory.
std::string dir = cmSystemTools::GetFilenamePath(obj.c_str()); std::string dir = cmSystemTools::GetFilenamePath(obj.c_str());
cmSystemTools::MakeDirectory(this->ConvertToFullPath(dir).c_str()); cmSystemTools::MakeDirectory(this->ConvertToFullPath(dir).c_str());
// If there is no dependencies file, create an empty one. // Generate the build-time dependencies file for this object file.
std::string depFileName = obj; std::string depMakeFile = this->GenerateDependsMakeFile(obj.c_str());
depFileName += ".depends.make";
std::string depFileNameFull = this->ConvertToFullPath(depFileName);
if(!cmSystemTools::FileExists(depFileNameFull.c_str()))
{
std::ofstream depFileStream(depFileNameFull.c_str());
this->WriteDisclaimer(depFileStream);
depFileStream
<< "# Empty dependencies file for object file " << obj.c_str() << ".\n"
<< "# This may be replaced when dependencies are built.\n";
}
// Open the rule file for writing. This should be copy-if-different // Open the rule file for writing. This should be copy-if-different
// because the rules may depend on this file itself. // because the rules may depend on this file itself.
@ -342,7 +339,7 @@ cmLocalUnixMakefileGenerator2
ruleFileStream ruleFileStream
<< "# Include any dependencies generated for this rule.\n" << "# Include any dependencies generated for this rule.\n"
<< m_IncludeDirective << " " << m_IncludeDirective << " "
<< this->ConvertToOutputForExisting(depFileName.c_str()).c_str() << this->ConvertToOutputForExisting(depMakeFile.c_str()).c_str()
<< "\n\n"; << "\n\n";
// Create the list of dependencies known at cmake time. These are // Create the list of dependencies known at cmake time. These are
@ -469,6 +466,33 @@ cmLocalUnixMakefileGenerator2
} }
} }
//----------------------------------------------------------------------------
std::string
cmLocalUnixMakefileGenerator2
::GenerateDependsMakeFile(const char* file)
{
// Check if the build-time dependencies file exists.
std::string depMarkFile = file;
depMarkFile += ".depends";
std::string depMakeFile = depMarkFile;
depMakeFile += ".make";
std::string depMakeFileFull = this->ConvertToFullPath(depMakeFile);
if(cmSystemTools::FileExists(depMakeFileFull.c_str()))
{
// The build-time dependencies file already exists. Check it.
this->CheckDependencies(m_Makefile->GetStartOutputDirectory(), file);
}
else
{
// The build-time dependencies file does not exist. Create an
// empty one.
std::string depMarkFileFull = this->ConvertToFullPath(depMarkFile);
this->WriteEmptyDependMakeFile(file, depMarkFileFull.c_str(),
depMakeFileFull.c_str());
}
return depMakeFile;
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void void
cmLocalUnixMakefileGenerator2 cmLocalUnixMakefileGenerator2
@ -496,7 +520,7 @@ cmLocalUnixMakefileGenerator2
m_Makefile->ExpandVariablesInString(replace); m_Makefile->ExpandVariablesInString(replace);
std::string::size_type lpos = 0; std::string::size_type lpos = 0;
std::string::size_type rpos; std::string::size_type rpos;
while((rpos = replace.find(lpos, '\n')) != std::string::npos) while((rpos = replace.find('\n', lpos)) != std::string::npos)
{ {
os << "# " << replace.substr(lpos, rpos-lpos); os << "# " << replace.substr(lpos, rpos-lpos);
lpos = rpos+1; lpos = rpos+1;
@ -667,7 +691,7 @@ cmLocalUnixMakefileGenerator2
cmakefileName += "/Makefile2.cmake"; cmakefileName += "/Makefile2.cmake";
std::string runRule = std::string runRule =
"@$(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)"; "@$(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)";
runRule += " --check-rerun "; runRule += " --check-build-system ";
runRule += this->ConvertToRelativeOutputPath(cmakefileName.c_str()); runRule += this->ConvertToRelativeOutputPath(cmakefileName.c_str());
// Write the main entry point target. This must be the VERY first // Write the main entry point target. This must be the VERY first
@ -676,7 +700,7 @@ cmLocalUnixMakefileGenerator2
std::vector<std::string> depends; std::vector<std::string> depends;
std::vector<std::string> commands; std::vector<std::string> commands;
// Check the build system in this directory. // Check the build system in this directory.
depends.push_back("cmake_check_rerun"); depends.push_back("cmake_check_build_system");
// Recursively build dependencies. // Recursively build dependencies.
commands.push_back(this->GetRecursiveMakeCall("all.depends")); commands.push_back(this->GetRecursiveMakeCall("all.depends"));
@ -699,8 +723,8 @@ cmLocalUnixMakefileGenerator2
postEcho.c_str()); postEcho.c_str());
} }
// Write special "cmake_check_rerun" target to run cmake with the // Write special "cmake_check_build_system" target to run cmake with
// --check-rerun flag. // the --check-build-system flag.
{ {
std::vector<std::string> no_depends; std::vector<std::string> no_depends;
std::vector<std::string> commands; std::vector<std::string> commands;
@ -709,7 +733,7 @@ cmLocalUnixMakefileGenerator2
"Special rule to run CMake to check the build system " "Special rule to run CMake to check the build system "
"integrity.", "integrity.",
"Checking build system integrity...", "Checking build system integrity...",
"cmake_check_rerun", "cmake_check_build_system",
no_depends, no_depends,
commands); commands);
} }
@ -1633,7 +1657,7 @@ cmLocalUnixMakefileGenerator2
commands.push_back(cmd); commands.push_back(cmd);
// Check the build system in destination directory. // Check the build system in destination directory.
commands.push_back(this->GetRecursiveMakeCall("cmake_check_rerun")); commands.push_back(this->GetRecursiveMakeCall("cmake_check_build_system"));
// Build the targets's dependencies. // Build the targets's dependencies.
commands.push_back(this->GetRecursiveMakeCall(dep.c_str())); commands.push_back(this->GetRecursiveMakeCall(dep.c_str()));
@ -1655,7 +1679,7 @@ cmLocalUnixMakefileGenerator2
// Check the build system in destination directory. // Check the build system in destination directory.
cmd += " && "; cmd += " && ";
cmd += this->GetRecursiveMakeCall("cmake_check_rerun"); cmd += this->GetRecursiveMakeCall("cmake_check_build_system");
// Build the targets's dependencies. // Build the targets's dependencies.
cmd += " && "; cmd += " && ";
@ -1736,7 +1760,9 @@ cmLocalUnixMakefileGenerator2ScanDependenciesC(
std::string line; std::string line;
while(cmSystemTools::GetLineFromStream(fin, line)) while(cmSystemTools::GetLineFromStream(fin, line))
{ {
// Match include directives. // Match include directives. TODO: Support include regex and
// ignore regex. Possibly also support directory-based inclusion
// in dependencies.
if(includeLine.find(line.c_str())) if(includeLine.find(line.c_str()))
{ {
// Get the file being included. // Get the file being included.
@ -1848,3 +1874,157 @@ cmLocalUnixMakefileGenerator2
return true; return true;
} }
//----------------------------------------------------------------------------
void
cmLocalUnixMakefileGenerator2
::CheckDependencies(const char* depCheck)
{
// Get the list of files to scan. This is given through the command
// line hook cmake file.
std::vector<std::string> files;
cmSystemTools::ExpandListArgument(depCheck, files);
// Check each file. The current working directory is already
// correct.
for(std::vector<std::string>::iterator f = files.begin();
f != files.end(); ++f)
{
cmLocalUnixMakefileGenerator2::CheckDependencies(".", f->c_str());
}
}
//----------------------------------------------------------------------------
void
cmLocalUnixMakefileGenerator2
::CheckDependencies(const char* dir, const char* file)
{
// Check the dependencies associated with the given file whose path
// is specified relative to the given directory. If any dependency
// is missing then dependencies should be regenerated.
bool regenerate = false;
// Construct the names of the mark and make files.
std::string depMarkFileFull = dir;
depMarkFileFull += "/";
depMarkFileFull += file;
depMarkFileFull += ".depends";
std::string depMakeFileFull = depMarkFileFull;
depMakeFileFull += ".make";
// Open the dependency makefile.
std::ifstream fin(depMakeFileFull.c_str());
if(fin)
{
// Parse dependencies.
std::string line;
std::string depender;
std::string dependee;
while(cmSystemTools::GetLineFromStream(fin, line))
{
// Skip empty lines and comments.
std::string::size_type pos = line.find_first_not_of(" \t\r\n");
if(pos == std::string::npos || line[pos] == '#')
{
continue;
}
// Strip leading whitespace.
if(pos > 0)
{
line = line.substr(pos);
}
// Skip lines too short to have a dependency.
if(line.size() < 2)
{
continue;
}
// Find the colon on the line. Skip the first two characters to
// avoid finding the colon in a drive letter on Windows. Ignore
// the line if a colon cannot be found.
if((pos = line.find(':', 2)) == std::string::npos)
{
continue;
}
// Split the line into depender and dependee.
depender = line.substr(0, pos);
dependee = line.substr(pos+1);
// Strip whitespace from the dependee.
if((pos = dependee.find_first_not_of(" \t\r\n")) != std::string::npos &&
pos > 0)
{
dependee = dependee.substr(pos);
}
if((pos = dependee.find_last_not_of(" \t\r\n")) != std::string::npos)
{
dependee = dependee.substr(0, pos+1);
}
// Convert dependee to a full path.
if(!cmSystemTools::FileIsFullPath(dependee.c_str()))
{
dependee = cmSystemTools::CollapseFullPath(dependee.c_str(), dir);
}
// If the dependee does not exist, we need to regenerate
// dependencies and the depender should be removed.
if(!cmSystemTools::FileExists(dependee.c_str()))
{
// Strip whitespace from the depender.
if((pos = depender.find_last_not_of(" \t\r\n")) != std::string::npos)
{
depender = depender.substr(0, pos+1);
}
// Convert depender to a full path.
if(!cmSystemTools::FileIsFullPath(depender.c_str()))
{
depender = cmSystemTools::CollapseFullPath(depender.c_str(), dir);
}
// Remove the depender.
cmSystemTools::RemoveFile(depender.c_str());
// Mark the need for regeneration.
regenerate = true;
}
}
}
else
{
// Could not open the dependencies file. It needs to be
// regenerated.
regenerate = true;
}
// If the dependencies file needs to be regenerated, create an empty
// one and delete the mark file.
if(regenerate)
{
cmLocalUnixMakefileGenerator2
::WriteEmptyDependMakeFile(file, depMarkFileFull.c_str(),
depMakeFileFull.c_str());
}
}
//----------------------------------------------------------------------------
void
cmLocalUnixMakefileGenerator2
::WriteEmptyDependMakeFile(const char* file,
const char* depMarkFileFull,
const char* depMakeFileFull)
{
// Remove the dependency mark file to be sure dependencies will be
// regenerated.
cmSystemTools::RemoveFile(depMarkFileFull);
// Write an empty dependency file.
std::ofstream depFileStream(depMakeFileFull);
depFileStream
<< "# Empty dependencies file for " << file << ".\n"
<< "# This may be replaced when dependencies are built.\n";
}

View File

@ -50,6 +50,9 @@ public:
/** Called from command-line hook to scan dependencies. */ /** Called from command-line hook to scan dependencies. */
static bool ScanDependencies(std::vector<std::string> const& args); static bool ScanDependencies(std::vector<std::string> const& args);
/** Called from command-line hook to check dependencies. */
static void CheckDependencies(const char* depCheck);
protected: protected:
void GenerateMakefile(); void GenerateMakefile();
@ -57,6 +60,7 @@ protected:
void GenerateTargetRuleFile(const cmTarget& target); void GenerateTargetRuleFile(const cmTarget& target);
void GenerateObjectRuleFile(const cmTarget& target, void GenerateObjectRuleFile(const cmTarget& target,
const cmSourceFile& source); const cmSourceFile& source);
std::string GenerateDependsMakeFile(const char* file);
void WriteMakeRule(std::ostream& os, void WriteMakeRule(std::ostream& os,
const char* comment, const char* comment,
const char* preEcho, const char* preEcho,
@ -112,6 +116,10 @@ protected:
static bool ScanDependenciesC(const char* objFile, const char* srcFile, static bool ScanDependenciesC(const char* objFile, const char* srcFile,
std::vector<std::string> const& includes); std::vector<std::string> const& includes);
static void CheckDependencies(const char* dir, const char* file);
static void WriteEmptyDependMakeFile(const char* file,
const char* depMarkFileFull,
const char* depMakeFileFull);
private: private:
// Map from target name to build directory containing it for // Map from target name to build directory containing it for
// jump-and-build targets. // jump-and-build targets.
@ -121,6 +129,9 @@ private:
std::string m_FilePath; std::string m_FilePath;
}; };
std::map<cmStdString, RemoteTarget> m_JumpAndBuild; std::map<cmStdString, RemoteTarget> m_JumpAndBuild;
// List the files for which to check dependency integrity.
std::set<cmStdString> m_CheckDependFiles;
}; };
#endif #endif

View File

@ -25,10 +25,6 @@
#if defined(CMAKE_BUILD_WITH_CMAKE) #if defined(CMAKE_BUILD_WITH_CMAKE)
# include "cmVariableWatch.h" # include "cmVariableWatch.h"
# include "cmVersion.h" # include "cmVersion.h"
#endif
#if defined(CMAKE_BUILD_WITH_CMAKE)
# include "cmLocalUnixMakefileGenerator2.h" # include "cmLocalUnixMakefileGenerator2.h"
#endif #endif
@ -317,9 +313,9 @@ void cmake::SetArgs(const std::vector<std::string>& args)
cmSystemTools::ConvertToUnixSlashes(path); cmSystemTools::ConvertToUnixSlashes(path);
this->SetHomeOutputDirectory(path.c_str()); this->SetHomeOutputDirectory(path.c_str());
} }
else if((i < args.size()-1) && (arg.find("--check-rerun",0) == 0)) else if((i < args.size()-1) && (arg.find("--check-build-system",0) == 0))
{ {
m_CheckRerun = args[++i]; m_CheckBuildSystem = args[++i];
} }
else if(arg.find("-V",0) == 0) else if(arg.find("-V",0) == 0)
{ {
@ -1268,8 +1264,8 @@ int cmake::Run(const std::vector<std::string>& args, bool noconfigure)
if(m_ScriptMode || !m_Local || !this->CacheVersionMatches() || if(m_ScriptMode || !m_Local || !this->CacheVersionMatches() ||
!cmSystemTools::FileExists(systemFile.c_str()) ) !cmSystemTools::FileExists(systemFile.c_str()) )
{ {
// Check whether we should really do a generate. // Check the state of the build system to see if we need to regenerate.
if(!this->CheckRerun()) if(!this->CheckBuildSystem())
{ {
return 0; return 0;
} }
@ -1572,16 +1568,23 @@ void cmake::UpdateConversionPathTable()
} }
} }
int cmake::CheckRerun() //----------------------------------------------------------------------------
int cmake::CheckBuildSystem()
{ {
// This method will check the integrity of the build system if the
// option was given on the command line. It reads the given file to
// determine whether CMake should rerun. If it does rerun then the
// generation step will check the integrity of dependencies. If it
// does not then we need to check the integrity here.
// If no file is provided for the check, we have to rerun. // If no file is provided for the check, we have to rerun.
if(m_CheckRerun.size() == 0) if(m_CheckBuildSystem.size() == 0)
{ {
return 1; return 1;
} }
// If the file provided does not exist, we have to rerun. // If the file provided does not exist, we have to rerun.
if(!cmSystemTools::FileExists(m_CheckRerun.c_str())) if(!cmSystemTools::FileExists(m_CheckBuildSystem.c_str()))
{ {
return 1; return 1;
} }
@ -1594,7 +1597,7 @@ int cmake::CheckRerun()
std::auto_ptr<cmLocalGenerator> lg(gg.CreateLocalGenerator()); std::auto_ptr<cmLocalGenerator> lg(gg.CreateLocalGenerator());
lg->SetGlobalGenerator(&gg); lg->SetGlobalGenerator(&gg);
cmMakefile* mf = lg->GetMakefile(); cmMakefile* mf = lg->GetMakefile();
if(!mf->ReadListFile(0, m_CheckRerun.c_str()) || if(!mf->ReadListFile(0, m_CheckBuildSystem.c_str()) ||
cmSystemTools::GetErrorOccuredFlag()) cmSystemTools::GetErrorOccuredFlag())
{ {
// There was an error reading the file. Just rerun. // There was an error reading the file. Just rerun.
@ -1630,6 +1633,14 @@ int cmake::CheckRerun()
} }
} }
#if defined(CMAKE_BUILD_WITH_CMAKE)
// We do not need to rerun CMake. Check dependency integrity.
if(const char* depCheck = mf->GetDefinition("CMAKE_DEPENDS_CHECK"))
{
cmLocalUnixMakefileGenerator2::CheckDependencies(depCheck);
}
#endif
// No need to rerun. // No need to rerun.
return 0; return 0;
} }

View File

@ -286,7 +286,12 @@ protected:
///! used by Run ///! used by Run
int LocalGenerate(); int LocalGenerate();
int CheckRerun();
/**
* Method called to check build system integrity at build time.
* Returns 1 if CMake should rerun and 0 otherwise.
*/
int CheckBuildSystem();
/** /**
* Generate CMAKE_ROOT and CMAKE_COMMAND cache entries * Generate CMAKE_ROOT and CMAKE_COMMAND cache entries
@ -307,7 +312,7 @@ private:
std::string m_CMakeCommand; std::string m_CMakeCommand;
std::string m_CXXEnvironment; std::string m_CXXEnvironment;
std::string m_CCEnvironment; std::string m_CCEnvironment;
std::string m_CheckRerun; std::string m_CheckBuildSystem;
bool m_DebugTryCompile; bool m_DebugTryCompile;
void UpdateConversionPathTable(); void UpdateConversionPathTable();