ENH: Split dependency scanning and checking into separate cmDepends superclass with language-specific subclasses such as cmDependsC.

This commit is contained in:
Brad King 2005-01-18 17:09:05 -05:00
parent ed9e0626fd
commit 195cdf172e
8 changed files with 617 additions and 329 deletions

View File

@ -46,6 +46,11 @@ SET(SRCS
cmake.h
cmakewizard.cxx
cmakewizard.h
cmDepends.h
cmDepends.cxx
cmDependsC.h
cmDependsC.cxx
)

96
Source/cmDepends.cxx Normal file
View File

@ -0,0 +1,96 @@
/*=========================================================================
Program: CMake - Cross-Platform Makefile Generator
Module: $RCSfile$
Language: C++
Date: $Date$
Version: $Revision$
Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#include "cmDepends.h"
#include "cmGeneratedFileStream.h"
#include "cmSystemTools.h"
//----------------------------------------------------------------------------
cmDepends::cmDepends(const char* dir, const char* targetFile):
m_Directory(dir),
m_TargetFile(targetFile),
m_DependsMakeFile(dir),
m_DependsMarkFile(dir)
{
// Construct the path to the make and mark files. Append
// appropriate extensions to their names.
m_DependsMakeFile += "/";
m_DependsMarkFile += "/";
m_DependsMakeFile += m_TargetFile;
m_DependsMarkFile += m_TargetFile;
m_DependsMakeFile += ".depends.make";
m_DependsMarkFile += ".depends";
}
//----------------------------------------------------------------------------
cmDepends::~cmDepends()
{
}
//----------------------------------------------------------------------------
void cmDepends::Write()
{
// Try to generate dependencies for the target file.
cmGeneratedFileStream fout(m_DependsMakeFile.c_str());
fout << "# Dependencies for " << m_TargetFile.c_str() << std::endl;
if(this->WriteDependencies(fout) && fout)
{
// Dependencies were generated. Touch the mark file.
std::ofstream fmark(m_DependsMarkFile.c_str());
fmark << "Dependencies updated for " << m_TargetFile.c_str() << std::endl;
}
}
//----------------------------------------------------------------------------
void cmDepends::Check()
{
// Check whether dependencies must be regenerated.
std::ifstream fin(m_DependsMakeFile.c_str());
if(!(fin && this->CheckDependencies(fin)))
{
// Clear all dependencies so they will be regenerated.
this->Clear();
}
}
//----------------------------------------------------------------------------
void cmDepends::Clear()
{
// Remove the dependency mark file to be sure dependencies will be
// regenerated.
cmSystemTools::RemoveFile(m_DependsMarkFile.c_str());
// Write an empty dependency file.
cmGeneratedFileStream depFileStream(m_DependsMakeFile.c_str());
depFileStream
<< "# Empty dependencies file for " << m_TargetFile.c_str() << ".\n"
<< "# This may be replaced when dependencies are built." << std::endl;
}
//----------------------------------------------------------------------------
const char* cmDepends::GetMakeFileName()
{
// Skip over the directory part of the name.
return m_DependsMakeFile.c_str() + m_Directory.length() + 1;
}
//----------------------------------------------------------------------------
const char* cmDepends::GetMarkFileName()
{
// Skip over the directory part of the name.
return m_DependsMarkFile.c_str() + m_Directory.length() + 1;
}

82
Source/cmDepends.h Normal file
View File

@ -0,0 +1,82 @@
/*=========================================================================
Program: CMake - Cross-Platform Makefile Generator
Module: $RCSfile$
Language: C++
Date: $Date$
Version: $Revision$
Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#ifndef cmDepends_h
#define cmDepends_h
#include "cmStandardIncludes.h"
/** \class cmDepends
* \brief Dependency scanner superclass.
*
* This class is responsible for maintaining a .depends.make file in
* the build tree corresponding to an object file. Subclasses help it
* maintain dependencies for particular languages.
*/
class cmDepends
{
public:
/** Instances need to know the build directory name and the relative
path from the build directory to the target file. */
cmDepends(const char* dir, const char* targetFile);
/** Virtual destructor to cleanup subclasses properly. */
virtual ~cmDepends();
/** Write dependencies for the target file. */
void Write();
/** Check dependencies for the target file. */
void Check();
/** Clear dependencies for the target file so they will be regenerated. */
void Clear();
/** Get the name of the dependency make file. */
const char* GetMakeFileName();
/** Get the name of the dependency mark file. */
const char* GetMarkFileName();
protected:
// Write dependencies for the target file to the given stream.
// Return true for success and false for failure.
virtual bool WriteDependencies(std::ostream& os)=0;
// Check dependencies for the target file in the given stream.
// Return false if dependencies must be regenerated and true
// otherwise.
virtual bool CheckDependencies(std::istream& is)=0;
// The directory in which the build rule for the target file is executed.
std::string m_Directory;
// The name of the target file for which dependencies are maintained.
std::string m_TargetFile;
// The name of the .depends.make file corresponding to the target.
std::string m_DependsMakeFile;
// The name of the .depends file marking when dependencies were generated.
std::string m_DependsMarkFile;
private:
cmDepends(cmDepends const&); // Purposely not implemented.
void operator=(cmDepends const&); // Purposely not implemented.
};
#endif

247
Source/cmDependsC.cxx Normal file
View File

@ -0,0 +1,247 @@
/*=========================================================================
Program: CMake - Cross-Platform Makefile Generator
Module: $RCSfile$
Language: C++
Date: $Date$
Version: $Revision$
Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#include "cmDependsC.h"
#include "cmSystemTools.h"
//----------------------------------------------------------------------------
cmDependsC::cmDependsC(const char* dir, const char* targetFile):
cmDepends(dir, targetFile),
m_SourceFile(),
m_IncludePath(0),
m_IncludeLineRegex()
{
}
//----------------------------------------------------------------------------
cmDependsC::cmDependsC(const char* dir, const char* targetFile,
const char* sourceFile,
std::vector<std::string> const& includes):
cmDepends(dir, targetFile),
m_SourceFile(sourceFile),
m_IncludePath(&includes),
m_IncludeLineRegex("^[ \t]*#[ \t]*include[ \t]*[<\"]([^\">]+)[\">]")
{
}
//----------------------------------------------------------------------------
cmDependsC::~cmDependsC()
{
}
//----------------------------------------------------------------------------
bool cmDependsC::WriteDependencies(std::ostream& os)
{
// Make sure this is a scanning instance.
if(m_SourceFile == "")
{
cmSystemTools::Error("Cannot scan dependencies without an source file.");
return false;
}
if(!m_IncludePath)
{
cmSystemTools::Error("Cannot scan dependencies without an include path.");
return false;
}
// Walk the dependency graph starting with the source file.
bool first = true;
m_Unscanned.push(m_SourceFile);
m_Encountered.insert(m_SourceFile);
std::set<cmStdString> dependencies;
std::set<cmStdString> scanned;
while(!m_Unscanned.empty())
{
// Get the next file to scan.
std::string fname = m_Unscanned.front();
m_Unscanned.pop();
// If not a full path, find the file in the include path.
std::string fullName;
if(first || cmSystemTools::FileIsFullPath(fname.c_str()))
{
fullName = fname;
}
else
{
for(std::vector<std::string>::const_iterator i = m_IncludePath->begin();
i != m_IncludePath->end(); ++i)
{
std::string temp = *i;
temp += "/";
temp += fname;
if(cmSystemTools::FileExists(temp.c_str()))
{
fullName = temp;
break;
}
}
}
// Scan the file if it was found and has not been scanned already.
if(fullName.size() && (scanned.find(fullName) == scanned.end()))
{
// Record scanned files.
scanned.insert(fullName);
// Try to scan the file. Just leave it out if we cannot find
// it.
std::ifstream fin(fullName.c_str());
if(fin)
{
// Add this file as a dependency.
dependencies.insert(fullName);
// Scan this file for new dependencies.
this->Scan(fin);
}
}
first = false;
}
m_Encountered.clear();
// Write the dependencies to the output stream.
for(std::set<cmStdString>::iterator i=dependencies.begin();
i != dependencies.end(); ++i)
{
os << m_TargetFile.c_str() << ": "
<< cmSystemTools::ConvertToOutputPath(i->c_str()).c_str()
<< std::endl;
}
os << std::endl;
return true;
}
//----------------------------------------------------------------------------
bool cmDependsC::CheckDependencies(std::istream& is)
{
// Parse dependencies from the stream. If any dependee is missing
// or newer than the depender then dependencies should be
// regenerated.
bool okay = true;
std::string line;
std::string depender;
std::string dependee;
while(cmSystemTools::GetLineFromStream(is, 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() < 3)
{
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(),
m_Directory.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(),
m_Directory.c_str());
}
// Dependencies must be regenerated if the dependee does not exist
// or if the depender exists and is older than the dependee.
int result = 0;
if(!cmSystemTools::FileExists(dependee.c_str()) ||
(cmSystemTools::FileExists(depender.c_str()) &&
(!cmSystemTools::FileTimeCompare(depender.c_str(), dependee.c_str(),
&result) || result < 0)))
{
// Dependencies must be regenerated.
okay = false;
// Remove the depender to be sure it is rebuilt.
cmSystemTools::RemoveFile(depender.c_str());
}
}
return okay;
}
//----------------------------------------------------------------------------
void cmDependsC::Scan(std::istream& is)
{
// Read one line at a time.
std::string line;
while(cmSystemTools::GetLineFromStream(is, line))
{
// Match include directives. TODO: Support include regex and
// ignore regex. Possibly also support directory-based inclusion
// in dependencies.
if(m_IncludeLineRegex.find(line.c_str()))
{
// Get the file being included.
std::string includeFile = m_IncludeLineRegex.match(1);
// Queue the file if it has not yet been encountered.
if(m_Encountered.find(includeFile) == m_Encountered.end())
{
m_Encountered.insert(includeFile);
m_Unscanned.push(includeFile);
}
}
}
}

70
Source/cmDependsC.h Normal file
View File

@ -0,0 +1,70 @@
/*=========================================================================
Program: CMake - Cross-Platform Makefile Generator
Module: $RCSfile$
Language: C++
Date: $Date$
Version: $Revision$
Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#ifndef cmDependsC_h
#define cmDependsC_h
#include "cmDepends.h"
#include <cmsys/RegularExpression.hxx>
#include <queue>
/** \class cmDependsC
* \brief Dependency scanner for C and C++ object files.
*/
class cmDependsC: public cmDepends
{
public:
/** Checking instances need to know the build directory name and the
relative path from the build directory to the target file. */
cmDependsC(const char* dir, const char* targetFile);
/** Scanning need to know the build directory name, the relative
path from the build directory to the target file, the source
file from which to start scanning, and the include file search
path. */
cmDependsC(const char* dir, const char* targetFile,
const char* sourceFile, std::vector<std::string> const& includes);
/** Virtual destructor to cleanup subclasses properly. */
virtual ~cmDependsC();
protected:
// Implement writing/checking methods required by superclass.
virtual bool WriteDependencies(std::ostream& os);
virtual bool CheckDependencies(std::istream& is);
// Method to scan a single file.
void Scan(std::istream& is);
// The source file from which to start scanning.
std::string m_SourceFile;
// The include file search path.
std::vector<std::string> const* m_IncludePath;
// Regular expression to identify C preprocessor include directives.
cmsys::RegularExpression m_IncludeLineRegex;
// Data structures for dependency graph walk.
std::set<cmStdString> m_Encountered;
std::queue<cmStdString> m_Unscanned;
private:
cmDependsC(cmDependsC const&); // Purposely not implemented.
void operator=(cmDependsC const&); // Purposely not implemented.
};
#endif

View File

@ -16,17 +16,20 @@
=========================================================================*/
#include "cmLocalUnixMakefileGenerator2.h"
#include "cmDepends.h"
#include "cmDependsC.h"
#include "cmGeneratedFileStream.h"
#include "cmGlobalGenerator.h"
#include "cmMakefile.h"
#include "cmSourceFile.h"
#include <memory> // auto_ptr
#include <queue>
#include <assert.h>
// Quick-switch for generating old makefiles.
#if 0
#if 1
# define CMLUMG_MAKEFILE_NAME "Makefile"
#else
# define CMLUMG_WRITE_OLD_MAKEFILE
@ -237,10 +240,26 @@ void cmLocalUnixMakefileGenerator2::GenerateCMakefile()
// 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)
<< "# The set of files whose dependency integrity should be checked:\n";
cmakefileStream
<< "SET(CMAKE_DEPENDS_LANGUAGES\n";
for(std::map<cmStdString, IntegrityCheckSet>::const_iterator
l = m_CheckDependFiles.begin();
l != m_CheckDependFiles.end(); ++l)
{
cmakefileStream
<< " \"" << l->first.c_str() << "\"\n";
}
cmakefileStream
<< " )\n";
for(std::map<cmStdString, IntegrityCheckSet>::const_iterator
l = m_CheckDependFiles.begin();
l != m_CheckDependFiles.end(); ++l)
{
cmakefileStream
<< "SET(CMAKE_DEPENDS_CHECK_" << l->first.c_str() << "\n";
for(std::set<cmStdString>::const_iterator i = l->second.begin();
i != l->second.end(); ++i)
{
cmakefileStream
<< " \"" << this->ConvertToRelativePath(i->c_str()).c_str() << "\"\n";
@ -248,6 +267,7 @@ void cmLocalUnixMakefileGenerator2::GenerateCMakefile()
cmakefileStream
<< " )\n";
}
}
//----------------------------------------------------------------------------
void
@ -278,7 +298,6 @@ cmLocalUnixMakefileGenerator2
std::string depBase = dir;
depBase += "/";
depBase += target.GetName();
std::string depMakeFile = this->GenerateDependsMakeFile(depBase.c_str());
// Construct the rule file name.
std::string ruleFileName = dir;
@ -302,13 +321,6 @@ cmLocalUnixMakefileGenerator2
ruleFileStream
<< "# Rule file for target " << target.GetName() << ".\n\n";
// Include the dependencies for the target.
ruleFileStream
<< "# Include any dependencies generated for this rule.\n"
<< m_IncludeDirective << " "
<< this->ConvertToOutputForExisting(depMakeFile.c_str()).c_str()
<< "\n\n";
// Include the rule file for each object.
if(!objects.empty())
{
@ -371,19 +383,27 @@ cmLocalUnixMakefileGenerator2
// Get the full path name of the object file.
std::string obj = this->GetObjectFileName(target, source);
// Save this in the target's list of object files.
objects.push_back(obj);
// The object file should be checked for dependency integrity.
m_CheckDependFiles.insert(obj);
// Create the directory containing the object file. This may be a
// subdirectory under the target's directory.
std::string dir = cmSystemTools::GetFilenamePath(obj.c_str());
cmSystemTools::MakeDirectory(this->ConvertToFullPath(dir).c_str());
// Generate the build-time dependencies file for this object file.
std::string depMakeFile = this->GenerateDependsMakeFile(obj.c_str());
std::string depMakeFile;
std::string depMarkFile;
if(!this->GenerateDependsMakeFile(lang, obj.c_str(),
depMakeFile, depMarkFile))
{
cmSystemTools::Error("No dependency checker available for language \"",
lang, "\".");
return;
}
// Save this in the target's list of object files.
objects.push_back(obj);
// The object file should be checked for dependency integrity.
m_CheckDependFiles[lang].insert(obj);
// Open the rule file for writing. This should be copy-if-different
// because the rules may depend on this file itself.
@ -424,8 +444,6 @@ cmLocalUnixMakefileGenerator2
depends.push_back(ruleFileName);
// Write the dependency generation rule.
std::string depTarget = obj;
depTarget += ".depends";
{
std::string depEcho = "Scanning ";
depEcho += lang;
@ -453,7 +471,7 @@ cmLocalUnixMakefileGenerator2
// Write the rule.
this->WriteMakeRule(ruleFileStream, 0, depEcho.c_str(),
depTarget.c_str(), depends, commands);
depMarkFile.c_str(), depends, commands);
}
// Write the build rule.
@ -676,30 +694,28 @@ cmLocalUnixMakefileGenerator2
}
//----------------------------------------------------------------------------
std::string
bool
cmLocalUnixMakefileGenerator2
::GenerateDependsMakeFile(const char* file)
::GenerateDependsMakeFile(const std::string& lang, const char* objFile,
std::string& depMakeFile, std::string& depMarkFile)
{
// 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()))
// Construct a checker for the given language.
std::auto_ptr<cmDepends>
checker(this->GetDependsChecker(lang,
m_Makefile->GetStartOutputDirectory(),
objFile));
if(checker.get())
{
// The build-time dependencies file already exists. Check it.
this->CheckDependencies(m_Makefile->GetStartOutputDirectory(), file);
// Save the make and mark file names.
depMakeFile = checker->GetMakeFileName();
depMarkFile = checker->GetMarkFileName();
// Check the dependencies.
checker->Check();
return true;
}
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;
return false;
}
//----------------------------------------------------------------------------
@ -2686,6 +2702,19 @@ cmLocalUnixMakefileGenerator2
}
}
//----------------------------------------------------------------------------
cmDepends*
cmLocalUnixMakefileGenerator2::GetDependsChecker(const std::string& lang,
const char* dir,
const char* objFile)
{
if(lang == "C" || lang == "CXX" || lang == "RC")
{
return new cmDependsC(dir, objFile);
}
return 0;
}
//----------------------------------------------------------------------------
bool
cmLocalUnixMakefileGenerator2
@ -2718,292 +2747,49 @@ cmLocalUnixMakefileGenerator2
if(lang == "C" || lang == "CXX" || lang == "RC")
{
// TODO: Handle RC (resource files) dependencies correctly.
return cmLocalUnixMakefileGenerator2::ScanDependenciesC(objFile, srcFile,
includes);
cmDependsC scanner(".", objFile, srcFile, includes);
scanner.Write();
return true;
}
return false;
}
//----------------------------------------------------------------------------
void
cmLocalUnixMakefileGenerator2ScanDependenciesC(
std::ifstream& fin,
std::set<cmStdString>& encountered,
std::queue<cmStdString>& unscanned)
void cmLocalUnixMakefileGenerator2::CheckDependencies(cmMakefile* mf)
{
// Regular expression to identify C preprocessor include directives.
cmsys::RegularExpression
includeLine("^[ \t]*#[ \t]*include[ \t]*[<\"]([^\">]+)[\">]");
// Get the list of languages that may have sources to check.
const char* langDef = mf->GetDefinition("CMAKE_DEPENDS_LANGUAGES");
if(!langDef)
{
return;
}
std::vector<std::string> languages;
cmSystemTools::ExpandListArgument(langDef, languages);
// Read one line at a time.
std::string line;
while(cmSystemTools::GetLineFromStream(fin, line))
// For each language get the set of files to check.
for(std::vector<std::string>::iterator l = languages.begin();
l != languages.end(); ++l)
{
// Match include directives. TODO: Support include regex and
// ignore regex. Possibly also support directory-based inclusion
// in dependencies.
if(includeLine.find(line.c_str()))
std::string depCheck = "CMAKE_DEPENDS_CHECK_";
depCheck += *l;
if(const char* fileDef = mf->GetDefinition(depCheck.c_str()))
{
// Get the file being included.
std::string includeFile = includeLine.match(1);
// Queue the file if it has not yet been encountered.
if(encountered.find(includeFile) == encountered.end())
{
encountered.insert(includeFile);
unscanned.push(includeFile);
}
}
}
}
//----------------------------------------------------------------------------
bool
cmLocalUnixMakefileGenerator2
::ScanDependenciesC(const char* objFile, const char* srcFile,
std::vector<std::string> const& includes)
{
// Walk the dependency graph starting with the source file.
std::set<cmStdString> dependencies;
std::set<cmStdString> encountered;
std::set<cmStdString> scanned;
std::queue<cmStdString> unscanned;
unscanned.push(srcFile);
encountered.insert(srcFile);
while(!unscanned.empty())
{
// Get the next file to scan.
std::string fname = unscanned.front();
unscanned.pop();
// If not a full path, find the file in the include path.
std::string fullName;
if(cmSystemTools::FileIsFullPath(fname.c_str()))
{
fullName = fname;
}
else
{
for(std::vector<std::string>::const_iterator i = includes.begin();
i != includes.end(); ++i)
{
std::string temp = *i;
temp += "/";
temp += fname;
if(cmSystemTools::FileExists(temp.c_str()))
{
fullName = temp;
break;
}
}
}
// Scan the file if it was found and has not been scanned already.
if(fullName.size() && (scanned.find(fullName) == scanned.end()))
{
// Record scanned files.
scanned.insert(fullName);
// Try to scan the file. Just leave it out if we cannot find
// it.
std::ifstream fin(fullName.c_str());
if(fin)
{
// Add this file as a dependency.
dependencies.insert(fullName);
// Scan this file for new dependencies.
cmLocalUnixMakefileGenerator2ScanDependenciesC(fin, encountered,
unscanned);
}
}
}
// Write the dependencies to the output file.
std::string depMarkFile = objFile;
std::string depMakeFile = objFile;
depMarkFile += ".depends";
depMakeFile += ".depends.make";
cmGeneratedFileStream fout(depMakeFile.c_str());
fout << "# Dependencies for " << objFile << std::endl;
for(std::set<cmStdString>::iterator i=dependencies.begin();
i != dependencies.end(); ++i)
{
fout << objFile << ": "
<< cmSystemTools::ConvertToOutputPath(i->c_str()).c_str()
<< std::endl;
}
fout << std::endl;
fout << "# Dependencies for " << depMarkFile.c_str() << std::endl;
for(std::set<cmStdString>::iterator i=dependencies.begin();
i != dependencies.end(); ++i)
{
fout << depMarkFile.c_str() << ": "
<< cmSystemTools::ConvertToOutputPath(i->c_str()).c_str()
<< std::endl;
}
// If we could write the dependencies, touch the corresponding
// depends file to mark dependencies up to date.
if(fout)
{
std::ofstream fmark(depMarkFile.c_str());
fmark << "Dependencies updated for " << objFile << "\n";
}
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.
std::vector<std::string> files;
cmSystemTools::ExpandListArgument(fileDef, files);
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)
// Construct a checker for the given language.
std::auto_ptr<cmDepends>
checker(cmLocalUnixMakefileGenerator2
::GetDependsChecker(*l, ".", f->c_str()));
if(checker.get())
{
// 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() < 3)
{
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;
checker->Check();
}
}
}
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.
cmGeneratedFileStream depFileStream(depMakeFileFull);
depFileStream
<< "# Empty dependencies file for " << file << ".\n"
<< "# This may be replaced when dependencies are built.\n";
}

View File

@ -21,6 +21,7 @@
class cmCustomCommand;
class cmDependInformation;
class cmDepends;
class cmMakeDepend;
class cmTarget;
class cmSourceFile;
@ -57,7 +58,7 @@ public:
static bool ScanDependencies(std::vector<std::string> const& args);
/** Called from command-line hook to check dependencies. */
static void CheckDependencies(const char* depCheck);
static void CheckDependencies(cmMakefile* mf);
protected:
@ -69,7 +70,10 @@ protected:
std::vector<std::string>& objects);
void GenerateCustomRuleFile(const cmCustomCommand& cc);
void GenerateUtilityRuleFile(const cmTarget& target);
std::string GenerateDependsMakeFile(const char* file);
bool GenerateDependsMakeFile(const std::string& lang,
const char* objFile,
std::string& depMakeFile,
std::string& depMarkFile);
void WriteMakeRule(std::ostream& os,
const char* comment,
const char* preEcho,
@ -163,12 +167,10 @@ protected:
std::string GetRecursiveMakeCall(const char* tgt);
void WriteJumpAndBuildRules(std::ostream& makefileStream);
static bool ScanDependenciesC(const char* objFile, const char* srcFile,
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);
static cmDepends* GetDependsChecker(const std::string& lang,
const char* dir,
const char* objFile);
private:
// Map from target name to build directory containing it for
// jump-and-build targets.
@ -179,8 +181,11 @@ private:
};
std::map<cmStdString, RemoteTarget> m_JumpAndBuild;
// List the files for which to check dependency integrity.
std::set<cmStdString> m_CheckDependFiles;
// List the files for which to check dependency integrity. Each
// language has its own list because integrity may be checked
// differently.
struct IntegrityCheckSet: public std::set<cmStdString> {};
std::map<cmStdString, IntegrityCheckSet> m_CheckDependFiles;
// Command used when a rule has no dependencies or commands.
std::vector<std::string> m_EmptyCommands;

View File

@ -1635,10 +1635,7 @@ int cmake::CheckBuildSystem()
#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);
}
cmLocalUnixMakefileGenerator2::CheckDependencies(mf);
#endif
// No need to rerun.