ENH: Added object file dependency scanning.

This commit is contained in:
Brad King 2004-10-26 12:53:51 -04:00
parent 163919a569
commit 1a4037c15a
2 changed files with 181 additions and 4 deletions

View File

@ -21,6 +21,8 @@
#include "cmMakefile.h"
#include "cmSourceFile.h"
#include <queue>
//----------------------------------------------------------------------------
cmLocalUnixMakefileGenerator2::cmLocalUnixMakefileGenerator2()
{
@ -414,8 +416,8 @@ cmLocalUnixMakefileGenerator2
<< "# This may be replaced when dependencies are built.\n";
}
// Open the rule file. This should be copy-if-different because the
// rules may depend on this file itself.
// Open the rule file for writing. This should be copy-if-different
// because the rules may depend on this file itself.
std::string ruleFileName = obj;
ruleFileName += ".make";
cmGeneratedFileStream ruleFile(ruleFileName.c_str());
@ -437,6 +439,10 @@ cmLocalUnixMakefileGenerator2
<< this->ConvertToOutputForExisting(depFileName.c_str()).c_str()
<< "\n\n";
// Identify the language of the source file.
const char* lang =
m_GlobalGenerator->GetLanguageFromExtension(source.GetSourceExtension().c_str());
// Write the dependency generation rule.
std::string depTarget = obj;
depTarget += ".depends";
@ -447,9 +453,22 @@ cmLocalUnixMakefileGenerator2
depComment += objName;
depends.push_back(source.GetFullPath());
depends.push_back(ruleFileName);
cmOStringStream depCmd;
// TODO: Account for source file properties and directory-level
// definitions.
depCmd << "$(CMAKE_COMMAND) -E cmake_depends " << lang << " "
<< this->ConvertToRelativeOutputPath(obj.c_str()) << " "
<< this->ConvertToRelativeOutputPath(source.GetFullPath().c_str());
std::vector<std::string> includeDirs;
this->GetIncludeDirectories(includeDirs);
for(std::vector<std::string>::iterator i = includeDirs.begin();
i != includeDirs.end(); ++i)
{
depCmd << " -I" << this->ConvertToRelativeOutputPath(i->c_str());
}
commands.push_back(depCmd.str());
std::string touchCmd = "@touch ";
touchCmd += this->ConvertToRelativeOutputPath(depTarget.c_str());
// TODO: Construct dependency generation rule and append command.
commands.push_back(touchCmd);
this->OutputMakeRule(ruleFileStream, depComment.c_str(), depTarget.c_str(),
depends, commands);
@ -461,7 +480,7 @@ cmLocalUnixMakefileGenerator2
std::vector<std::string> commands;
std::string buildComment = "object ";
buildComment += objName;
depends.push_back(depTarget);
depends.push_back(source.GetFullPath());
depends.push_back(ruleFileName);
std::string touchCmd = "@touch ";
touchCmd += this->ConvertToRelativeOutputPath(obj.c_str());
@ -525,3 +544,154 @@ cmLocalUnixMakefileGenerator2
source.GetSourceExtension().c_str());
return objectName;
}
//----------------------------------------------------------------------------
bool
cmLocalUnixMakefileGenerator2
::ScanDependencies(std::vector<std::string> const& args)
{
// Format of arguments is:
// $(CMAKE_COMMAND), cmake_depends, <lang>, <obj>, <src>, [include-flags]
// The caller has ensured that all required arguments exist.
// The file to which to write dependencies.
const char* objFile = args[3].c_str();
// The source file at which to start the scan.
const char* srcFile = args[4].c_str();
// Convert the include flags to full paths.
std::vector<std::string> includes;
for(unsigned int i=5; i < args.size(); ++i)
{
if(args[i].substr(0, 2) == "-I")
{
// Get the include path without the -I flag.
std::string inc = args[i].substr(2);
includes.push_back(cmSystemTools::CollapseFullPath(inc.c_str()));
}
}
// Dispatch the scan for each language.
std::string const& lang = args[2];
if(lang == "C" || lang == "CXX")
{
return cmLocalUnixMakefileGenerator2::ScanDependenciesC(objFile, srcFile,
includes);
}
return false;
}
//----------------------------------------------------------------------------
void
cmLocalUnixMakefileGenerator2ScanDependenciesC(
std::ifstream& fin,
std::set<cmStdString>& encountered,
std::queue<cmStdString>& unscanned)
{
// Regular expression to identify C preprocessor include directives.
cmsys::RegularExpression
includeLine("^[ \t]*#[ \t]*include[ \t]*[<\"]([^\">]+)[\">]");
// Read one line at a time.
std::string line;
while(cmSystemTools::GetLineFromStream(fin, line))
{
// Match include directives.
if(includeLine.find(line.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 has not been scanned already.
if(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 depMakeFile = objFile;
depMakeFile += ".depends.make";
std::ofstream fout(depMakeFile.c_str());
fout << "# Dependencies for " << objFile << endl;
for(std::set<cmStdString>::iterator i=dependencies.begin();
i != dependencies.end(); ++i)
{
fout << objFile << " : " << i->c_str() << endl;
}
fout << endl;
fout << "# Dependencies for " << objFile << ".depends" << endl;
for(std::set<cmStdString>::iterator i=dependencies.begin();
i != dependencies.end(); ++i)
{
fout << objFile << ".depends : " << i->c_str() << endl;
}
return true;
}

View File

@ -46,6 +46,10 @@ public:
* makefiles. This is done by a direct invocation from make.
*/
virtual void Generate(bool fromTheTop);
/** Called from command-line hook to scan dependencies. */
static bool ScanDependencies(std::vector<std::string> const& args);
protected:
void GenerateMakefile();
@ -57,6 +61,9 @@ protected:
std::string GetTargetDirectory(const cmTarget& target);
std::string GetObjectFileName(const cmSourceFile& source);
static bool ScanDependenciesC(const char* objFile, const char* srcFile,
std::vector<std::string> const& includes);
private:
};