Merge topic 'FixImplicitDepends2'

05f162c AddCustomCommand: Handle multiple IMPLICIT_DEPENDS files (#10048)
c66f03a cmDepends: No dependency-vector erasure in CheckDependencies
e74ff7c cmDepends: allow multiple dependees per depender
ecc77d0 cmDependsC: fix indentation
3e7d97d cmDependsC: remove code duplication
b4e8f49 cmDependsC: remove unused member variable
This commit is contained in:
Brad King 2012-11-06 14:13:58 -05:00 committed by CMake Topic Stage
commit 3be0a7b4bf
13 changed files with 207 additions and 178 deletions

View File

@ -68,7 +68,8 @@ public:
" [COMMAND command2 [ARGS] [args2...] ...]\n" " [COMMAND command2 [ARGS] [args2...] ...]\n"
" [MAIN_DEPENDENCY depend]\n" " [MAIN_DEPENDENCY depend]\n"
" [DEPENDS [depends...]]\n" " [DEPENDS [depends...]]\n"
" [IMPLICIT_DEPENDS <lang1> depend1 ...]\n" " [IMPLICIT_DEPENDS <lang1> depend1\n"
" [<lang2> depend2] ...]\n"
" [WORKING_DIRECTORY dir]\n" " [WORKING_DIRECTORY dir]\n"
" [COMMENT comment] [VERBATIM] [APPEND])\n" " [COMMENT comment] [VERBATIM] [APPEND])\n"
"This defines a command to generate specified OUTPUT file(s). " "This defines a command to generate specified OUTPUT file(s). "
@ -142,6 +143,8 @@ public:
"dependencies of an input file. The language given specifies the " "dependencies of an input file. The language given specifies the "
"programming language whose corresponding dependency scanner should " "programming language whose corresponding dependency scanner should "
"be used. Currently only C and CXX language scanners are supported. " "be used. Currently only C and CXX language scanners are supported. "
"The language has to be specified for every file in the "
"IMPLICIT_DEPENDS list. "
"Dependencies discovered from the scanning are added to those of " "Dependencies discovered from the scanning are added to those of "
"the custom command at build time. Note that the IMPLICIT_DEPENDS " "the custom command at build time. Note that the IMPLICIT_DEPENDS "
"option is currently supported only for Makefile generators and " "option is currently supported only for Makefile generators and "

View File

@ -50,6 +50,7 @@ bool cmDepends::Write(std::ostream &makeDepends,
std::vector<std::string> pairs; std::vector<std::string> pairs;
cmSystemTools::ExpandListArgument(srcStr, pairs); cmSystemTools::ExpandListArgument(srcStr, pairs);
std::map<std::string, std::set<std::string> > dependencies;
for(std::vector<std::string>::iterator si = pairs.begin(); for(std::vector<std::string>::iterator si = pairs.begin();
si != pairs.end();) si != pairs.end();)
{ {
@ -62,9 +63,14 @@ bool cmDepends::Write(std::ostream &makeDepends,
obj = this->LocalGenerator->Convert(obj.c_str(), obj = this->LocalGenerator->Convert(obj.c_str(),
cmLocalGenerator::HOME_OUTPUT, cmLocalGenerator::HOME_OUTPUT,
cmLocalGenerator::MAKEFILE); cmLocalGenerator::MAKEFILE);
dependencies[obj].insert(src);
}
for(std::map<std::string, std::set<std::string> >::const_iterator
it = dependencies.begin(); it != dependencies.end(); ++it)
{
// Write the dependencies for this pair. // Write the dependencies for this pair.
if(!this->WriteDependencies(src.c_str(), obj.c_str(), if(!this->WriteDependencies(it->second, it->first,
makeDepends, internalDepends)) makeDepends, internalDepends))
{ {
return false; return false;
@ -134,8 +140,9 @@ void cmDepends::Clear(const char *file)
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
bool cmDepends::WriteDependencies(const char*, const char*, bool cmDepends::WriteDependencies(
std::ostream&, std::ostream&) const std::set<std::string>&, const std::string&,
std::ostream&, std::ostream&)
{ {
// This should be implemented by the subclass. // This should be implemented by the subclass.
return false; return false;
@ -174,8 +181,10 @@ bool cmDepends::CheckDependencies(std::istream& internalDepends,
// kdelibs/khtml this reduces the number of calls from 184k down to 92k, // kdelibs/khtml this reduces the number of calls from 184k down to 92k,
// or the time for cmake -E cmake_depends from 0.3 s down to 0.21 s. // or the time for cmake -E cmake_depends from 0.3 s down to 0.21 s.
dependerExists = cmSystemTools::FileExists(this->Depender); dependerExists = cmSystemTools::FileExists(this->Depender);
DependencyVector tmp; // If we erase validDeps[this->Depender] by overwriting it with an empty
validDeps[this->Depender] = tmp; // vector, we lose dependencies for dependers that have multiple
// entries. No need to initialize the entry, std::map will do so on first
// access.
currentDependencies = &validDeps[this->Depender]; currentDependencies = &validDeps[this->Depender];
continue; continue;
} }

View File

@ -76,8 +76,10 @@ protected:
// Write dependencies for the target file to the given stream. // Write dependencies for the target file to the given stream.
// Return true for success and false for failure. // Return true for success and false for failure.
virtual bool WriteDependencies(const char *src, const char* obj, virtual bool WriteDependencies(const std::set<std::string>& sources,
std::ostream& makeDepends, std::ostream& internalDepends); const std::string& obj,
std::ostream& makeDepends,
std::ostream& internalDepends);
// Check dependencies for the target file in the given stream. // Check dependencies for the target file in the given stream.
// Return false if dependencies must be regenerated and true // Return false if dependencies must be regenerated and true

View File

@ -98,176 +98,179 @@ cmDependsC::~cmDependsC()
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
bool cmDependsC::WriteDependencies(const char *src, const char *obj, bool cmDependsC::WriteDependencies(const std::set<std::string>& sources,
std::ostream& makeDepends, std::ostream& internalDepends) const std::string& obj,
std::ostream& makeDepends,
std::ostream& internalDepends)
{ {
// Make sure this is a scanning instance. // Make sure this is a scanning instance.
if(!src || src[0] == '\0') if(sources.empty() || sources.begin()->empty())
{ {
cmSystemTools::Error("Cannot scan dependencies without a source file."); cmSystemTools::Error("Cannot scan dependencies without a source file.");
return false; return false;
} }
if(!obj || obj[0] == '\0') if(obj.empty())
{ {
cmSystemTools::Error("Cannot scan dependencies without an object file."); cmSystemTools::Error("Cannot scan dependencies without an object file.");
return false; return false;
} }
std::set<cmStdString> dependencies;
bool haveDeps = false;
if (this->ValidDeps != 0) if (this->ValidDeps != 0)
{ {
std::map<std::string, DependencyVector>::const_iterator tmpIt = std::map<std::string, DependencyVector>::const_iterator tmpIt =
this->ValidDeps->find(obj); this->ValidDeps->find(obj);
if (tmpIt!= this->ValidDeps->end()) if (tmpIt!= this->ValidDeps->end())
{ {
// Write the dependencies to the output stream. Makefile rules
// written by the original local generator for this directory
// convert the dependencies to paths relative to the home output
// directory. We must do the same here.
internalDepends << obj << std::endl;
for(DependencyVector::const_iterator i=tmpIt->second.begin(); for(DependencyVector::const_iterator i=tmpIt->second.begin();
i != tmpIt->second.end(); ++i) i != tmpIt->second.end(); ++i)
{ {
makeDepends << obj << ": " << dependencies.insert(*i);
this->LocalGenerator->Convert(i->c_str(),
cmLocalGenerator::HOME_OUTPUT,
cmLocalGenerator::MAKEFILE)
<< std::endl;
internalDepends << " " << i->c_str() << std::endl;
} }
makeDepends << std::endl; haveDeps = true;
return true;
} }
} }
// Walk the dependency graph starting with the source file. if (!haveDeps)
bool first = true;
UnscannedEntry root;
root.FileName = src;
this->Unscanned.push(root);
this->Encountered.clear();
this->Encountered.insert(src);
std::set<cmStdString> dependencies;
std::set<cmStdString> scanned;
// Use reserve to allocate enough memory for tempPathStr
// so that during the loops no memory is allocated or freed
std::string tempPathStr;
tempPathStr.reserve(4*1024);
while(!this->Unscanned.empty())
{ {
// Get the next file to scan. // Walk the dependency graph starting with the source file.
UnscannedEntry current = this->Unscanned.front(); int srcFiles = (int)sources.size();
this->Unscanned.pop(); this->Encountered.clear();
// If not a full path, find the file in the include path. for(std::set<std::string>::const_iterator srcIt = sources.begin();
std::string fullName; srcIt != sources.end(); ++srcIt)
if(first || cmSystemTools::FileIsFullPath(current.FileName.c_str()))
{ {
if(cmSystemTools::FileExists(current.FileName.c_str(), true)) UnscannedEntry root;
{ root.FileName = *srcIt;
fullName = current.FileName; this->Unscanned.push(root);
} this->Encountered.insert(*srcIt);
} }
else if(!current.QuotedLocation.empty() &&
cmSystemTools::FileExists(current.QuotedLocation.c_str(), true))
{
// The include statement producing this entry was a double-quote
// include and the included file is present in the directory of
// the source containing the include statement.
fullName = current.QuotedLocation;
}
else
{
std::map<cmStdString, cmStdString>::iterator
headerLocationIt=this->HeaderLocationCache.find(current.FileName);
if (headerLocationIt!=this->HeaderLocationCache.end())
{
fullName=headerLocationIt->second;
}
else for(std::vector<std::string>::const_iterator i =
this->IncludePath.begin(); i != this->IncludePath.end(); ++i)
{
// Construct the name of the file as if it were in the current
// include directory. Avoid using a leading "./".
tempPathStr = ""; std::set<cmStdString> scanned;
if((*i) == ".")
{
tempPathStr += current.FileName;
}
else
{
tempPathStr += *i;
tempPathStr+="/";
tempPathStr+=current.FileName;
}
// Look for the file in this location. // Use reserve to allocate enough memory for tempPathStr
if(cmSystemTools::FileExists(tempPathStr.c_str(), true)) // so that during the loops no memory is allocated or freed
std::string tempPathStr;
tempPathStr.reserve(4*1024);
while(!this->Unscanned.empty())
{
// Get the next file to scan.
UnscannedEntry current = this->Unscanned.front();
this->Unscanned.pop();
// If not a full path, find the file in the include path.
std::string fullName;
if((srcFiles>0)
|| cmSystemTools::FileIsFullPath(current.FileName.c_str()))
{
if(cmSystemTools::FileExists(current.FileName.c_str(), true))
{ {
fullName = tempPathStr; fullName = current.FileName;
HeaderLocationCache[current.FileName]=fullName;
break;
} }
} }
} else if(!current.QuotedLocation.empty() &&
cmSystemTools::FileExists(current.QuotedLocation.c_str(), true))
// Complain if the file cannot be found and matches the complain
// regex.
if(fullName.empty() &&
this->IncludeRegexComplain.find(current.FileName.c_str()))
{
cmSystemTools::Error("Cannot find file \"",
current.FileName.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);
// Check whether this file is already in the cache
std::map<cmStdString, cmIncludeLines*>::iterator fileIt=
this->FileCache.find(fullName);
if (fileIt!=this->FileCache.end())
{ {
fileIt->second->Used=true; // The include statement producing this entry was a double-quote
dependencies.insert(fullName); // include and the included file is present in the directory of
for (std::vector<UnscannedEntry>::const_iterator incIt= // the source containing the include statement.
fileIt->second->UnscannedEntries.begin(); fullName = current.QuotedLocation;
incIt!=fileIt->second->UnscannedEntries.end(); ++incIt)
{
if (this->Encountered.find(incIt->FileName) ==
this->Encountered.end())
{
this->Encountered.insert(incIt->FileName);
this->Unscanned.push(*incIt);
}
}
} }
else else
{ {
std::map<cmStdString, cmStdString>::iterator
// Try to scan the file. Just leave it out if we cannot find headerLocationIt=this->HeaderLocationCache.find(current.FileName);
// it. if (headerLocationIt!=this->HeaderLocationCache.end())
std::ifstream fin(fullName.c_str());
if(fin)
{ {
// Add this file as a dependency. fullName=headerLocationIt->second;
dependencies.insert(fullName); }
else for(std::vector<std::string>::const_iterator i =
this->IncludePath.begin(); i != this->IncludePath.end(); ++i)
{
// Construct the name of the file as if it were in the current
// include directory. Avoid using a leading "./".
// Scan this file for new dependencies. Pass the directory tempPathStr = "";
// containing the file to handle double-quote includes. if((*i) == ".")
std::string dir = cmSystemTools::GetFilenamePath(fullName); {
this->Scan(fin, dir.c_str(), fullName); tempPathStr += current.FileName;
}
else
{
tempPathStr += *i;
tempPathStr+="/";
tempPathStr+=current.FileName;
}
// Look for the file in this location.
if(cmSystemTools::FileExists(tempPathStr.c_str(), true))
{
fullName = tempPathStr;
HeaderLocationCache[current.FileName]=fullName;
break;
}
} }
} }
}
first = false; // Complain if the file cannot be found and matches the complain
// regex.
if(fullName.empty() &&
this->IncludeRegexComplain.find(current.FileName.c_str()))
{
cmSystemTools::Error("Cannot find file \"",
current.FileName.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);
// Check whether this file is already in the cache
std::map<cmStdString, cmIncludeLines*>::iterator fileIt=
this->FileCache.find(fullName);
if (fileIt!=this->FileCache.end())
{
fileIt->second->Used=true;
dependencies.insert(fullName);
for (std::vector<UnscannedEntry>::const_iterator incIt=
fileIt->second->UnscannedEntries.begin();
incIt!=fileIt->second->UnscannedEntries.end(); ++incIt)
{
if (this->Encountered.find(incIt->FileName) ==
this->Encountered.end())
{
this->Encountered.insert(incIt->FileName);
this->Unscanned.push(*incIt);
}
}
}
else
{
// 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. Pass the directory
// containing the file to handle double-quote includes.
std::string dir = cmSystemTools::GetFilenamePath(fullName);
this->Scan(fin, dir.c_str(), fullName);
}
}
}
srcFiles--;
}
} }
// Write the dependencies to the output stream. Makefile rules // Write the dependencies to the output stream. Makefile rules
@ -275,7 +278,7 @@ bool cmDependsC::WriteDependencies(const char *src, const char *obj,
// convert the dependencies to paths relative to the home output // convert the dependencies to paths relative to the home output
// directory. We must do the same here. // directory. We must do the same here.
internalDepends << obj << std::endl; internalDepends << obj << std::endl;
for(std::set<cmStdString>::iterator i=dependencies.begin(); for(std::set<cmStdString>::const_iterator i=dependencies.begin();
i != dependencies.end(); ++i) i != dependencies.end(); ++i)
{ {
makeDepends << obj << ": " << makeDepends << obj << ": " <<

View File

@ -32,11 +32,9 @@ public:
virtual ~cmDependsC(); virtual ~cmDependsC();
protected: protected:
typedef std::vector<char> t_CharBuffer;
// Implement writing/checking methods required by superclass. // Implement writing/checking methods required by superclass.
virtual bool WriteDependencies(const char *src, virtual bool WriteDependencies(const std::set<std::string>& sources,
const char *file, const std::string& obj,
std::ostream& makeDepends, std::ostream& makeDepends,
std::ostream& internalDepends); std::ostream& internalDepends);
@ -82,7 +80,6 @@ protected:
const std::map<std::string, DependencyVector>* ValidDeps; const std::map<std::string, DependencyVector>* ValidDeps;
std::set<cmStdString> Encountered; std::set<cmStdString> Encountered;
std::queue<UnscannedEntry> Unscanned; std::queue<UnscannedEntry> Unscanned;
t_CharBuffer Buffer;
std::map<cmStdString, cmIncludeLines *> FileCache; std::map<cmStdString, cmIncludeLines *> FileCache;
std::map<cmStdString, cmStdString> HeaderLocationCache; std::map<cmStdString, cmStdString> HeaderLocationCache;

View File

@ -170,44 +170,50 @@ cmDependsFortran::~cmDependsFortran()
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
bool cmDependsFortran::WriteDependencies(const char *src, const char *obj, bool cmDependsFortran::WriteDependencies(
std::ostream&, std::ostream&) const std::set<std::string>& sources, const std::string& obj,
std::ostream&, std::ostream&)
{ {
// Make sure this is a scanning instance. // Make sure this is a scanning instance.
if(!src || src[0] == '\0') if(sources.empty() || sources.begin()->empty())
{ {
cmSystemTools::Error("Cannot scan dependencies without an source file."); cmSystemTools::Error("Cannot scan dependencies without a source file.");
return false; return false;
} }
if(!obj || obj[0] == '\0') if(obj.empty())
{ {
cmSystemTools::Error("Cannot scan dependencies without an object file."); cmSystemTools::Error("Cannot scan dependencies without an object file.");
return false; return false;
} }
// Get the information object for this source. bool okay = true;
cmDependsFortranSourceInfo& info = for(std::set<std::string>::const_iterator it = sources.begin();
this->Internal->CreateObjectInfo(obj, src); it != sources.end(); ++it)
// Make a copy of the macros defined via ADD_DEFINITIONS
std::set<std::string> ppDefines(this->PPDefinitions.begin(),
this->PPDefinitions.end());
// Create the parser object. The constructor takes ppMacro and info per
// reference, so we may look into the resulting objects later.
cmDependsFortranParser parser(this, ppDefines, info);
// Push on the starting file.
cmDependsFortranParser_FilePush(&parser, src);
// Parse the translation unit.
if(cmDependsFortran_yyparse(parser.Scanner) != 0)
{ {
// Failed to parse the file. Report failure to write dependencies. const std::string& src = *it;
return false; // Get the information object for this source.
} cmDependsFortranSourceInfo& info =
this->Internal->CreateObjectInfo(obj.c_str(), src.c_str());
return true; // Make a copy of the macros defined via ADD_DEFINITIONS
std::set<std::string> ppDefines(this->PPDefinitions.begin(),
this->PPDefinitions.end());
// Create the parser object. The constructor takes ppMacro and info per
// reference, so we may look into the resulting objects later.
cmDependsFortranParser parser(this, ppDefines, info);
// Push on the starting file.
cmDependsFortranParser_FilePush(&parser, src.c_str());
// Parse the translation unit.
if(cmDependsFortran_yyparse(parser.Scanner) != 0)
{
// Failed to parse the file. Report failure to write dependencies.
okay = false;
}
}
return okay;
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------

View File

@ -66,7 +66,7 @@ protected:
// Implement writing/checking methods required by superclass. // Implement writing/checking methods required by superclass.
virtual bool WriteDependencies( virtual bool WriteDependencies(
const char *src, const char *file, const std::set<std::string>& sources, const std::string& file,
std::ostream& makeDepends, std::ostream& internalDepends); std::ostream& makeDepends, std::ostream& internalDepends);
// Actually write the depenencies to the streams. // Actually write the depenencies to the streams.

View File

@ -25,11 +25,11 @@ cmDependsJava::~cmDependsJava()
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
bool cmDependsJava::WriteDependencies(const char *src, const char *, bool cmDependsJava::WriteDependencies(const std::set<std::string>& sources,
std::ostream&, std::ostream&) const std::string&, std::ostream&, std::ostream&)
{ {
// Make sure this is a scanning instance. // Make sure this is a scanning instance.
if(!src || src[0] == '\0') if(sources.empty() || sources.begin()->empty())
{ {
cmSystemTools::Error("Cannot scan dependencies without an source file."); cmSystemTools::Error("Cannot scan dependencies without an source file.");
return false; return false;

View File

@ -29,7 +29,8 @@ public:
protected: protected:
// Implement writing/checking methods required by superclass. // Implement writing/checking methods required by superclass.
virtual bool WriteDependencies(const char *src, const char *file, virtual bool WriteDependencies(
const std::set<std::string>& sources, const std::string& file,
std::ostream& makeDepends, std::ostream& internalDepends); std::ostream& makeDepends, std::ostream& internalDepends);
virtual bool CheckDependencies(std::istream& internalDepends, virtual bool CheckDependencies(std::istream& internalDepends,
const char* internalDependsFileName, const char* internalDependsFileName,

View File

@ -1939,8 +1939,12 @@ void cmLocalUnixMakefileGenerator3
for(ImplicitDependFileMap::const_iterator pi = implicitPairs.begin(); for(ImplicitDependFileMap::const_iterator pi = implicitPairs.begin();
pi != implicitPairs.end(); ++pi) pi != implicitPairs.end(); ++pi)
{ {
cmakefileStream << " \"" << pi->second << "\" "; for(cmDepends::DependencyVector::const_iterator di = pi->second.begin();
cmakefileStream << "\"" << pi->first << "\"\n"; di != pi->second.end(); ++ di)
{
cmakefileStream << " \"" << *di << "\" ";
cmakefileStream << "\"" << pi->first << "\"\n";
}
} }
cmakefileStream << " )\n"; cmakefileStream << " )\n";
@ -2204,7 +2208,7 @@ cmLocalUnixMakefileGenerator3::AddImplicitDepends(cmTarget const& tgt,
const char* obj, const char* obj,
const char* src) const char* src)
{ {
this->ImplicitDepends[tgt.GetName()][lang][obj] = src; this->ImplicitDepends[tgt.GetName()][lang][obj].push_back(src);
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------

View File

@ -209,7 +209,8 @@ public:
// File pairs for implicit dependency scanning. The key of the map // File pairs for implicit dependency scanning. The key of the map
// is the depender and the value is the explicit dependee. // is the depender and the value is the explicit dependee.
struct ImplicitDependFileMap: public std::map<cmStdString, cmStdString> {}; struct ImplicitDependFileMap:
public std::map<cmStdString, cmDepends::DependencyVector> {};
struct ImplicitDependLanguageMap: struct ImplicitDependLanguageMap:
public std::map<cmStdString, ImplicitDependFileMap> {}; public std::map<cmStdString, ImplicitDependFileMap> {};
struct ImplicitDependTargetMap: struct ImplicitDependTargetMap:

View File

@ -64,7 +64,8 @@ if("${CMAKE_GENERATOR}" MATCHES "Make")
# Test the IMPLICIT_DEPENDS feature. # Test the IMPLICIT_DEPENDS feature.
set(ZOT_DEPENDS IMPLICIT_DEPENDS CXX ${CMAKE_CURRENT_SOURCE_DIR}/dep.cxx) set(ZOT_DEPENDS IMPLICIT_DEPENDS CXX ${CMAKE_CURRENT_SOURCE_DIR}/dep.cxx)
set(ZOT_CUSTOM_DEP set(ZOT_CUSTOM_DEP
IMPLICIT_DEPENDS CXX ${CMAKE_CURRENT_SOURCE_DIR}/dep_custom.cxx) IMPLICIT_DEPENDS CXX ${CMAKE_CURRENT_SOURCE_DIR}/dep_custom.cxx
CXX ${CMAKE_CURRENT_SOURCE_DIR}/dep_custom2.cxx )
else() else()
# No IMPLICIT_DEPENDS...just depend directly. # No IMPLICIT_DEPENDS...just depend directly.
set(ZOT_DEPENDS DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/zot.hxx.in) set(ZOT_DEPENDS DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/zot.hxx.in)

View File

@ -0,0 +1,2 @@
#include <zot_custom.hxx.in>
// some comment