ENH: Split dependency scanning and checking into separate cmDepends superclass with language-specific subclasses such as cmDependsC.
This commit is contained in:
parent
ed9e0626fd
commit
195cdf172e
|
@ -46,6 +46,11 @@ SET(SRCS
|
|||
cmake.h
|
||||
cmakewizard.cxx
|
||||
cmakewizard.h
|
||||
|
||||
cmDepends.h
|
||||
cmDepends.cxx
|
||||
cmDependsC.h
|
||||
cmDependsC.cxx
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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,16 +240,33 @@ 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";
|
||||
}
|
||||
cmakefileStream
|
||||
<< " )\n";
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
@ -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());
|
||||
// Construct a checker for the given language.
|
||||
std::auto_ptr<cmDepends>
|
||||
checker(cmLocalUnixMakefileGenerator2
|
||||
::GetDependsChecker(*l, ".", f->c_str()));
|
||||
if(checker.get())
|
||||
{
|
||||
checker->Check();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
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() < 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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";
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue