CMake/Source/cmDependsC.cxx

261 lines
7.5 KiB
C++

/*=========================================================================
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_IncludeRegexLine(),
m_IncludeRegexScan(),
m_IncludeRegexComplain()
{
}
//----------------------------------------------------------------------------
cmDependsC::cmDependsC(const char* dir, const char* targetFile,
const char* sourceFile,
std::vector<std::string> const& includes,
const char* scanRegex, const char* complainRegex):
cmDepends(dir, targetFile),
m_SourceFile(sourceFile),
m_IncludePath(&includes),
m_IncludeRegexLine("^[ \t]*#[ \t]*include[ \t]*[<\"]([^\">]+)[\">]"),
m_IncludeRegexScan(scanRegex),
m_IncludeRegexComplain(complainRegex)
{
}
//----------------------------------------------------------------------------
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.clear();
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()))
{
if(cmSystemTools::FileExists(fname.c_str()))
{
fullName = fname;
}
}
else
{
for(std::vector<std::string>::const_iterator i = m_IncludePath->begin();
i != m_IncludePath->end(); ++i)
{
// Construct the name of the file as if it were in the current
// include directory. Avoid using a leading "./".
std::string temp = *i;
if(temp == ".")
{
temp = "";
}
else
{
temp += "/";
}
temp += fname;
// Look for the file in this location.
if(cmSystemTools::FileExists(temp.c_str()))
{
fullName = temp;
break;
}
}
}
// Complain if the file cannot be found and matches the complain
// regex.
if(fullName.empty() && m_IncludeRegexComplain.find(fname.c_str()))
{
cmSystemTools::Error("Cannot find file \"", fname.c_str(), "\".");
return false;
}
// Scan the file if it was found and has not been scanned already.
if(!fullName.empty() && (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;
}
// 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);
}
// Strip whitespace from the depender.
if((pos = depender.find_last_not_of(" \t\r\n")) != std::string::npos)
{
depender = depender.substr(0, pos+1);
}
// 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.
if(m_IncludeRegexLine.find(line.c_str()))
{
// Get the file being included.
std::string includeFile = m_IncludeRegexLine.match(1);
// Queue the file if it has not yet been encountered and it
// matches the regular expression for recursive scanning.
if(m_Encountered.find(includeFile) == m_Encountered.end() &&
m_IncludeRegexScan.find(includeFile.c_str()))
{
m_Encountered.insert(includeFile);
m_Unscanned.push(includeFile);
}
}
}
}