ENH: Add library dependency analysis.
This commit is contained in:
parent
d53458de9a
commit
1f8df8585e
|
@ -162,6 +162,14 @@ IF(BUILD_TESTING)
|
||||||
${CMake_BINARY_DIR}/Tests/Wrapping/bin
|
${CMake_BINARY_DIR}/Tests/Wrapping/bin
|
||||||
TestDriverTest subdir/test3)
|
TestDriverTest subdir/test3)
|
||||||
|
|
||||||
|
ADD_TEST(dependency ${CMake_BINARY_DIR}/Source/cmaketest
|
||||||
|
${CMake_SOURCE_DIR}/Tests/Dependency
|
||||||
|
${CMake_BINARY_DIR}/Tests/Dependency
|
||||||
|
exec
|
||||||
|
${CMake_BINARY_DIR}/Tests/Dependency/Exec
|
||||||
|
Dependency)
|
||||||
|
|
||||||
|
ENDIF (DART_ROOT)
|
||||||
ENDIF(BUILD_TESTING)
|
ENDIF(BUILD_TESTING)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -653,8 +653,8 @@ void cmDSPWriter::WriteDSPHeader(std::ostream& fout, const char *libName,
|
||||||
libMultiLineOptions += " \n";
|
libMultiLineOptions += " \n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::vector<std::string>::iterator i;
|
std::vector<std::string>::const_iterator i;
|
||||||
std::vector<std::string>& libdirs = m_Makefile->GetLinkDirectories();
|
const std::vector<std::string>& libdirs = target.GetLinkDirectories();
|
||||||
for(i = libdirs.begin(); i != libdirs.end(); ++i)
|
for(i = libdirs.begin(); i != libdirs.end(); ++i)
|
||||||
{
|
{
|
||||||
std::string path = *i;
|
std::string path = *i;
|
||||||
|
|
|
@ -169,8 +169,7 @@ void cmDSWWriter::WriteDSWFile(std::ostream& fout)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
l->second.GetLinkLibraries().push_back(
|
l->second.AddLinkLibrary(al->first, cmTarget::GENERAL);
|
||||||
cmTarget::LinkLibraries::value_type(al->first, cmTarget::GENERAL));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,9 +240,7 @@ void cmMSDotNETGenerator::WriteSLNFile(std::ostream& fout)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
l->second.GetLinkLibraries().push_back(
|
l->second.AddLinkLibrary(al->first,cmTarget::GENERAL);
|
||||||
cmTarget::LinkLibraries::value_type(al->first,
|
|
||||||
cmTarget::GENERAL));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -907,7 +905,7 @@ void cmMSDotNETGenerator::OutputModuleDefinitionFile(std::ostream& fout,
|
||||||
void cmMSDotNETGenerator::OutputLibraryDirectories(std::ostream& fout,
|
void cmMSDotNETGenerator::OutputLibraryDirectories(std::ostream& fout,
|
||||||
const char*,
|
const char*,
|
||||||
const char*,
|
const char*,
|
||||||
const cmTarget &)
|
const cmTarget &tgt)
|
||||||
{
|
{
|
||||||
bool hasone = false;
|
bool hasone = false;
|
||||||
if(m_LibraryOutputPath.size())
|
if(m_LibraryOutputPath.size())
|
||||||
|
@ -922,8 +920,8 @@ void cmMSDotNETGenerator::OutputLibraryDirectories(std::ostream& fout,
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<std::string> pathEmitted;
|
std::set<std::string> pathEmitted;
|
||||||
std::vector<std::string>::iterator i;
|
std::vector<std::string>::const_iterator i;
|
||||||
std::vector<std::string>& libdirs = m_Makefile->GetLinkDirectories();
|
const std::vector<std::string>& libdirs = tgt.GetLinkDirectories();
|
||||||
for(i = libdirs.begin(); i != libdirs.end(); ++i)
|
for(i = libdirs.begin(); i != libdirs.end(); ++i)
|
||||||
{
|
{
|
||||||
std::string lpath = *i;
|
std::string lpath = *i;
|
||||||
|
|
|
@ -429,6 +429,8 @@ void cmMakefile::GenerateMakefile()
|
||||||
{
|
{
|
||||||
l->second.GenerateSourceFilesFromSourceLists(*this);
|
l->second.GenerateSourceFilesFromSourceLists(*this);
|
||||||
l->second.MergeLibraries(m_LinkLibraries);
|
l->second.MergeLibraries(m_LinkLibraries);
|
||||||
|
l->second.MergeDirectories(m_LinkDirectories);
|
||||||
|
l->second.AnalyzeLibDependencies(*this);
|
||||||
}
|
}
|
||||||
// now do the generation
|
// now do the generation
|
||||||
m_MakefileGenerator->GenerateMakefile();
|
m_MakefileGenerator->GenerateMakefile();
|
||||||
|
@ -521,9 +523,7 @@ void cmMakefile::AddLinkLibraryForTarget(const char *target,
|
||||||
{
|
{
|
||||||
if (m_Targets.find(target) != m_Targets.end())
|
if (m_Targets.find(target) != m_Targets.end())
|
||||||
{
|
{
|
||||||
m_Targets[target].GetLinkLibraries().
|
m_Targets[target].AddLinkLibrary( *this, target, lib, llt );
|
||||||
push_back(
|
|
||||||
std::pair<std::string, cmTarget::LinkLibraryType>(lib,llt));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -695,6 +695,15 @@ void cmMakefile::AddLibrary(const char* lname, int shared,
|
||||||
"Whether a library is static, shared or module.",
|
"Whether a library is static, shared or module.",
|
||||||
cmCacheManager::INTERNAL);
|
cmCacheManager::INTERNAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear its dependencies. Otherwise, dependencies might persist
|
||||||
|
// over changes in CMakeLists.txt, making the information stale and
|
||||||
|
// hence useless.
|
||||||
|
std::string depname = lname;
|
||||||
|
depname += "_LIB_DEPENDS";
|
||||||
|
cmCacheManager::GetInstance()->
|
||||||
|
AddCacheEntry(depname.c_str(), "",
|
||||||
|
"Dependencies for target", cmCacheManager::INTERNAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmMakefile::AddExecutable(const char *exeName,
|
void cmMakefile::AddExecutable(const char *exeName,
|
||||||
|
@ -861,7 +870,7 @@ void cmMakefile::ExpandVariables()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cmMakefile::IsOn(const char* name)
|
bool cmMakefile::IsOn(const char* name) const
|
||||||
{
|
{
|
||||||
const char* value = this->GetDefinition(name);
|
const char* value = this->GetDefinition(name);
|
||||||
return cmSystemTools::IsOn(value);
|
return cmSystemTools::IsOn(value);
|
||||||
|
|
|
@ -390,14 +390,6 @@ public:
|
||||||
return m_IncludeDirectories;
|
return m_IncludeDirectories;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a list of link directories in the build.
|
|
||||||
*/
|
|
||||||
std::vector<std::string>& GetLinkDirectories()
|
|
||||||
{
|
|
||||||
return m_LinkDirectories;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a list of source files in this makefile.
|
* Return a list of source files in this makefile.
|
||||||
*/
|
*/
|
||||||
|
@ -440,7 +432,7 @@ public:
|
||||||
/** Test a boolean cache entry to see if it is true or false,
|
/** Test a boolean cache entry to see if it is true or false,
|
||||||
* returns false if no entry defined.
|
* returns false if no entry defined.
|
||||||
*/
|
*/
|
||||||
bool IsOn(const char* name);
|
bool IsOn(const char* name) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of preprocessor define flags.
|
* Get a list of preprocessor define flags.
|
||||||
|
|
|
@ -578,8 +578,8 @@ void cmNMakeMakefileGenerator::OutputLinkLibraries(std::ostream& fout,
|
||||||
|
|
||||||
if (lib_path_opt.size())
|
if (lib_path_opt.size())
|
||||||
{
|
{
|
||||||
std::vector<std::string>& libdirs = m_Makefile->GetLinkDirectories();
|
const std::vector<std::string>& libdirs = tgt.GetLinkDirectories();
|
||||||
for(std::vector<std::string>::iterator libDir = libdirs.begin();
|
for(std::vector<std::string>::const_iterator libDir = libdirs.begin();
|
||||||
libDir != libdirs.end(); ++libDir)
|
libDir != libdirs.end(); ++libDir)
|
||||||
{
|
{
|
||||||
std::string libpath = ShortPath(libDir->c_str());
|
std::string libpath = ShortPath(libDir->c_str());
|
||||||
|
|
|
@ -17,6 +17,10 @@
|
||||||
#include "cmTarget.h"
|
#include "cmTarget.h"
|
||||||
#include "cmMakefile.h"
|
#include "cmMakefile.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
|
||||||
void cmTarget::GenerateSourceFilesFromSourceLists( cmMakefile &mf)
|
void cmTarget::GenerateSourceFilesFromSourceLists( cmMakefile &mf)
|
||||||
{
|
{
|
||||||
// this is only done for non install targets
|
// this is only done for non install targets
|
||||||
|
@ -59,20 +63,52 @@ void cmTarget::GenerateSourceFilesFromSourceLists( cmMakefile &mf)
|
||||||
LinkLibraries::iterator p = m_LinkLibraries.begin();
|
LinkLibraries::iterator p = m_LinkLibraries.begin();
|
||||||
for (;p != m_LinkLibraries.end(); ++p)
|
for (;p != m_LinkLibraries.end(); ++p)
|
||||||
{
|
{
|
||||||
mf.ExpandVariablesInString(p->first);
|
mf.ExpandVariablesInString(p->first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmTarget::MergeLibraries(const LinkLibraries &ll)
|
void cmTarget::MergeLibraries(const LinkLibraries &ll)
|
||||||
{
|
{
|
||||||
typedef std::vector<std::pair<std::string,LinkLibraryType> > LinkLibraries;
|
m_LinkLibraries.insert( m_LinkLibraries.end(), ll.begin(), ll.end() );
|
||||||
|
}
|
||||||
|
|
||||||
LinkLibraries::const_iterator p = ll.begin();
|
void cmTarget::MergeDirectories(const std::vector<std::string> &ld)
|
||||||
for (;p != ll.end(); ++p)
|
{
|
||||||
|
m_LinkDirectories.insert( m_LinkDirectories.end(), ld.begin(), ld.end() );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void cmTarget::AddLinkLibrary(const std::string& lib,
|
||||||
|
LinkLibraryType llt)
|
||||||
|
{
|
||||||
|
m_LinkLibraries.push_back( std::pair<std::string, cmTarget::LinkLibraryType>(lib,llt) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmTarget::AddLinkLibrary(cmMakefile& mf,
|
||||||
|
const char *target, const char* lib,
|
||||||
|
LinkLibraryType llt)
|
||||||
|
{
|
||||||
|
m_LinkLibraries.push_back( std::pair<std::string, cmTarget::LinkLibraryType>(lib,llt) );
|
||||||
|
|
||||||
|
// Add the explicit dependency information for this target. This is
|
||||||
|
// simply a set of libraries separated by ";". There should always
|
||||||
|
// be a trailing ";". These library names are not canonical, in that
|
||||||
|
// they may be "-framework x", "-ly", "/path/libz.a", etc.
|
||||||
|
std::string cache_name( target );
|
||||||
|
cache_name += "_LIB_DEPENDS";
|
||||||
|
std::string dependencies;
|
||||||
|
const char* old_val = mf.GetDefinition( cache_name.c_str() );
|
||||||
|
if( old_val )
|
||||||
{
|
{
|
||||||
m_LinkLibraries.push_back(*p);
|
dependencies += old_val;
|
||||||
}
|
}
|
||||||
|
if( dependencies.find( lib ) == std::string::npos )
|
||||||
|
{
|
||||||
|
dependencies += lib;
|
||||||
|
dependencies += ";";
|
||||||
|
}
|
||||||
|
mf.AddCacheDefinition( cache_name.c_str(), dependencies.c_str(),
|
||||||
|
"Dependencies for the target", cmCacheManager::INTERNAL );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cmTarget::HasCxx() const
|
bool cmTarget::HasCxx() const
|
||||||
|
@ -87,3 +123,264 @@ bool cmTarget::HasCxx() const
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
cmTarget::AnalyzeLibDependencies( const cmMakefile& mf )
|
||||||
|
{
|
||||||
|
typedef std::map< std::string, std::pair<std::string,LinkLibraryType> > LibMap;
|
||||||
|
typedef std::vector< std::string > LinkLine;
|
||||||
|
|
||||||
|
// Maps the canonical names to the full objects of m_LinkLibraries.
|
||||||
|
LibMap lib_map;
|
||||||
|
|
||||||
|
// The list canonical names in the order they were orginally
|
||||||
|
// specified on the link line (m_LinkLibraries).
|
||||||
|
LinkLine lib_order;
|
||||||
|
|
||||||
|
// The dependency maps.
|
||||||
|
DependencyMap dep_map, dep_map_implicit;
|
||||||
|
|
||||||
|
LinkLibraries::iterator lib;
|
||||||
|
for(lib = m_LinkLibraries.begin(); lib != m_LinkLibraries.end(); ++lib)
|
||||||
|
{
|
||||||
|
// skip zero size library entries, this may happen
|
||||||
|
// if a variable expands to nothing.
|
||||||
|
if (lib->first.size() == 0) continue;
|
||||||
|
|
||||||
|
std::string cname = CanonicalLibraryName(lib->first);
|
||||||
|
lib_map[ cname ] = *lib;
|
||||||
|
lib_order.push_back( cname );
|
||||||
|
}
|
||||||
|
|
||||||
|
// First, get the explicit dependencies for those libraries that
|
||||||
|
// have specified them
|
||||||
|
for( LibMap::iterator i = lib_map.begin(); i != lib_map.end(); ++i )
|
||||||
|
{
|
||||||
|
GatherDependencies( mf, i->first, dep_map );
|
||||||
|
}
|
||||||
|
|
||||||
|
// For the rest, get implicit dependencies. A library x depends
|
||||||
|
// implicitly on a library y if x appears before y on the link
|
||||||
|
// line. However, x does not implicitly depend on y if y
|
||||||
|
// *explicitly* depends on x [*1]--such cyclic dependencies must be
|
||||||
|
// explicitly specified. Note that implicit dependency cycles can
|
||||||
|
// still occur: "-lx -ly -lx" will generate a implicit dependency
|
||||||
|
// cycle provided that neither x nor y have explicit dependencies.
|
||||||
|
//
|
||||||
|
// [*1] This prevents external libraries from depending on libraries
|
||||||
|
// generated by this project.
|
||||||
|
|
||||||
|
for( LibMap::iterator i = lib_map.begin(); i != lib_map.end(); ++i )
|
||||||
|
{
|
||||||
|
if( dep_map.find( i->first ) == dep_map.end() )
|
||||||
|
{
|
||||||
|
LinkLine::iterator pos = std::find( lib_order.begin(), lib_order.end(), i->first );
|
||||||
|
for( ; pos != lib_order.end(); ++pos )
|
||||||
|
{
|
||||||
|
std::set<std::string> visited;
|
||||||
|
if( !DependsOn( *pos, i->first, dep_map, visited ) )
|
||||||
|
{
|
||||||
|
dep_map_implicit[ i->first ].insert( *pos );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dep_map_implicit[ i->first ].erase( i->first ); // cannot depend on itself
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine all the depedency information
|
||||||
|
// dep_map.insert( dep_map_implicit.begin(), dep_map_implicit.end() );
|
||||||
|
// doesn't work under MSVC++.
|
||||||
|
for( DependencyMap::iterator i = dep_map_implicit.begin();
|
||||||
|
i != dep_map_implicit.end(); ++i )
|
||||||
|
{
|
||||||
|
dep_map[ i->first ] = i->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new link line order.
|
||||||
|
std::set<std::string> done, visited;
|
||||||
|
std::vector<std::string> link_line;
|
||||||
|
for( LibMap::iterator i = lib_map.begin(); i != lib_map.end(); ++i )
|
||||||
|
{
|
||||||
|
Emit( i->first, dep_map, done, visited, link_line );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If LIBRARY_OUTPUT_PATH is not set, then we must add search paths
|
||||||
|
// for all the new libraries added by the dependency analysis.
|
||||||
|
const char* libOutPath = mf.GetDefinition("LIBRARY_OUTPUT_PATH");
|
||||||
|
bool addLibDirs = (libOutPath==0 || strcmp(libOutPath,"")==0);
|
||||||
|
|
||||||
|
m_LinkLibraries.clear();
|
||||||
|
for( std::vector<std::string>::reverse_iterator i = link_line.rbegin();
|
||||||
|
i != link_line.rend(); ++i )
|
||||||
|
{
|
||||||
|
// Some of the libraries in the new link line may not have been in
|
||||||
|
// the orginal link line, but were added by the dependency
|
||||||
|
// analysis. For these libraries, we assume the GENERAL type and
|
||||||
|
// add the name of the library.
|
||||||
|
if( lib_map.find(*i) == lib_map.end() )
|
||||||
|
{
|
||||||
|
if( addLibDirs )
|
||||||
|
{
|
||||||
|
const char* libpath = mf.GetDefinition( i->c_str() );
|
||||||
|
if( libpath )
|
||||||
|
{
|
||||||
|
// Don't add a link directory that is already present.
|
||||||
|
if(std::find(m_LinkDirectories.begin(),
|
||||||
|
m_LinkDirectories.end(), libpath) == m_LinkDirectories.end())
|
||||||
|
{
|
||||||
|
m_LinkDirectories.push_back(libpath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_LinkLibraries.push_back( std::make_pair(*i,GENERAL) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_LinkLibraries.push_back( lib_map[ *i ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
std::string cmTarget::CanonicalLibraryName( const std::string& lib ) const
|
||||||
|
{
|
||||||
|
cmRegularExpression reg("((^[ \t]*\\-l)|(^[ \t]*\\-framework[ \t]*))(.+)");
|
||||||
|
if(lib.find('/') != std::string::npos
|
||||||
|
&& !reg.find(lib))
|
||||||
|
{
|
||||||
|
std::string dir, file;
|
||||||
|
cmSystemTools::SplitProgramPath(lib.c_str(),
|
||||||
|
dir, file);
|
||||||
|
cmRegularExpression libname("lib(.*)(\\.so|\\.sl|\\.a|\\.dylib).*");
|
||||||
|
cmRegularExpression libname_noprefix("(.*)(\\.so|\\.sl|\\.a|\\.dylib|\\.lib).*");
|
||||||
|
if(libname.find(file))
|
||||||
|
{
|
||||||
|
return libname.match(1);
|
||||||
|
}
|
||||||
|
else if(libname_noprefix.find(file))
|
||||||
|
{
|
||||||
|
return libname_noprefix.match(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(!reg.find(lib))
|
||||||
|
{
|
||||||
|
return lib;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return reg.match(4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void cmTarget::Emit( const std::string& lib,
|
||||||
|
const DependencyMap& dep_map,
|
||||||
|
std::set<std::string>& emitted,
|
||||||
|
std::set<std::string>& visited,
|
||||||
|
std::vector<std::string>& link_line ) const
|
||||||
|
{
|
||||||
|
// It's already been emitted
|
||||||
|
if( emitted.find(lib) != emitted.end() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If this library hasn't been visited before, then emit all its
|
||||||
|
// dependencies before emitting the library itself. If it has been
|
||||||
|
// visited before, then there is a dependency cycle. Just emit the
|
||||||
|
// library itself, and let the recursion that got us here deal with
|
||||||
|
// emitting the dependencies for the library.
|
||||||
|
|
||||||
|
if( visited.insert(lib).second )
|
||||||
|
{
|
||||||
|
if( dep_map.find(lib) != dep_map.end() ) // does it have dependencies?
|
||||||
|
{
|
||||||
|
const std::set<std::string>& dep_on = dep_map.find( lib )->second;
|
||||||
|
std::set<std::string>::const_iterator i;
|
||||||
|
for( i = dep_on.begin(); i != dep_on.end(); ++i )
|
||||||
|
{
|
||||||
|
Emit( *i, dep_map, emitted, visited, link_line );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
link_line.push_back( lib );
|
||||||
|
emitted.insert(lib);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void cmTarget::GatherDependencies( const cmMakefile& mf,
|
||||||
|
const std::string& lib,
|
||||||
|
DependencyMap& dep_map ) const
|
||||||
|
{
|
||||||
|
// If the library is already in the dependency map, then it has
|
||||||
|
// already been fully processed.
|
||||||
|
if( dep_map.find(lib) != dep_map.end() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const char* deps = mf.GetDefinition( (lib+"_LIB_DEPENDS").c_str() );
|
||||||
|
if( deps )
|
||||||
|
{
|
||||||
|
// Make sure this library is in the map, even if it has an empty
|
||||||
|
// set of dependencies. This distinguishes the case of explicitly
|
||||||
|
// no dependencies with that of unspecified dependencies.
|
||||||
|
dep_map[lib];
|
||||||
|
|
||||||
|
// Parse the dependency information, which is simply a set of
|
||||||
|
// libraries separated by ";". There is always a trailing ";".
|
||||||
|
std::string depline = deps;
|
||||||
|
std::string::size_type start = 0;
|
||||||
|
std::string::size_type end;
|
||||||
|
end = depline.find( ";", start );
|
||||||
|
while( end != std::string::npos )
|
||||||
|
{
|
||||||
|
std::string l = depline.substr( start, end-start );
|
||||||
|
if( l.size() != 0 )
|
||||||
|
{
|
||||||
|
const std::string cname = CanonicalLibraryName(l);
|
||||||
|
dep_map[ lib ].insert( cname );
|
||||||
|
GatherDependencies( mf, cname, dep_map );
|
||||||
|
}
|
||||||
|
start = end+1; // skip the ;
|
||||||
|
end = depline.find( ";", start );
|
||||||
|
}
|
||||||
|
dep_map[lib].erase(lib); // cannot depend on itself
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool cmTarget::DependsOn( const std::string& lib1, const std::string& lib2,
|
||||||
|
const DependencyMap& dep_map,
|
||||||
|
std::set<std::string>& visited ) const
|
||||||
|
{
|
||||||
|
if( !visited.insert( lib1 ).second )
|
||||||
|
return false; // already visited here
|
||||||
|
|
||||||
|
if( lib1 == lib2 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( dep_map.find(lib1) == dep_map.end() )
|
||||||
|
return false; // lib1 doesn't have any dependencies
|
||||||
|
|
||||||
|
const std::set<std::string>& dep_set = dep_map.find(lib1)->second;
|
||||||
|
|
||||||
|
if( dep_set.end() != dep_set.find( lib2 ) )
|
||||||
|
return true; // lib1 doesn't directly depend on lib2.
|
||||||
|
|
||||||
|
// Do a recursive check: does lib1 depend on x which depends on lib2?
|
||||||
|
for( std::set<std::string>::const_iterator itr = dep_set.begin();
|
||||||
|
itr != dep_set.end(); ++itr )
|
||||||
|
{
|
||||||
|
if( DependsOn( *itr, lib2, dep_map, visited ) )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
@ -79,7 +79,8 @@ public:
|
||||||
enum LinkLibraryType {GENERAL, DEBUG, OPTIMIZED};
|
enum LinkLibraryType {GENERAL, DEBUG, OPTIMIZED};
|
||||||
typedef std::vector<std::pair<std::string,LinkLibraryType> > LinkLibraries;
|
typedef std::vector<std::pair<std::string,LinkLibraryType> > LinkLibraries;
|
||||||
const LinkLibraries &GetLinkLibraries() const {return m_LinkLibraries;}
|
const LinkLibraries &GetLinkLibraries() const {return m_LinkLibraries;}
|
||||||
LinkLibraries &GetLinkLibraries() {return m_LinkLibraries;}
|
|
||||||
|
const std::vector<std::string>& GetLinkDirectories() const {return m_LinkDirectories;}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the path where this target should be installed. This is relative to
|
* Set the path where this target should be installed. This is relative to
|
||||||
|
@ -88,10 +89,22 @@ public:
|
||||||
std::string GetInstallPath() const {return m_InstallPath;}
|
std::string GetInstallPath() const {return m_InstallPath;}
|
||||||
void SetInstallPath(const char *name) {m_InstallPath = name;}
|
void SetInstallPath(const char *name) {m_InstallPath = name;}
|
||||||
|
|
||||||
|
void AddLinkLibrary(cmMakefile& mf,
|
||||||
|
const char *target, const char* lib,
|
||||||
|
LinkLibraryType llt);
|
||||||
|
|
||||||
|
void AddLinkLibrary(const std::string& lib,
|
||||||
|
LinkLibraryType llt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge Link Libraries into this targets current list
|
* Merge Link Libraries into this targets current list
|
||||||
*/
|
*/
|
||||||
void MergeLibraries(const LinkLibraries &ll);
|
void MergeLibraries(const LinkLibraries &ll);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge Link Directories into this targets current list
|
||||||
|
*/
|
||||||
|
void MergeDirectories(const std::vector<std::string> &ld);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the SourceFilesList from the SourceLists. This should only be
|
* Generate the SourceFilesList from the SourceLists. This should only be
|
||||||
|
@ -107,12 +120,59 @@ public:
|
||||||
///! Get the utilities used by this target
|
///! Get the utilities used by this target
|
||||||
std::set<std::string>const& GetUtilities() const { return m_Utilities; }
|
std::set<std::string>const& GetUtilities() const { return m_Utilities; }
|
||||||
|
|
||||||
|
void AnalyzeLibDependencies( const cmMakefile& mf );
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* This map holds the dependency graph. map[x] returns a set of
|
||||||
|
* direct dependencies of x.
|
||||||
|
*/
|
||||||
|
typedef std::map< std::string, std::set< std::string > > DependencyMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For each library in the link line, return a canonical name. The
|
||||||
|
* orginal library names have complicated forms, such as "x",
|
||||||
|
* "libx.so", "/full/path/libx.a", "-lx", and "-framework x".
|
||||||
|
*/
|
||||||
|
std::string CanonicalLibraryName( const std::string& lib ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits the library \param lib and all its dependencies into
|
||||||
|
* link_line. \param emitted keeps track of the libraries that have
|
||||||
|
* been emitted to avoid duplicates--it is more efficient than
|
||||||
|
* searching link_line. \param visited is used detect cycles. Note
|
||||||
|
* that \param link_line is in reverse order, in that the
|
||||||
|
* dependencies of a library are listed before the library itself.
|
||||||
|
*/
|
||||||
|
void Emit( const std::string& lib,
|
||||||
|
const DependencyMap& dep_map,
|
||||||
|
std::set<std::string>& emitted,
|
||||||
|
std::set<std::string>& visited,
|
||||||
|
std::vector<std::string>& link_line ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the explicit dependencies for \param lib, if they have been
|
||||||
|
* specified, and inserts them into \param dep_map.
|
||||||
|
*/
|
||||||
|
void GatherDependencies( const cmMakefile& mf, const std::string& lib,
|
||||||
|
DependencyMap& dep_map ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if lib1 depends on lib2 according to \param
|
||||||
|
* dep_map. \param visited is used to prevent infinite loops when
|
||||||
|
* cycles are present.
|
||||||
|
*/
|
||||||
|
bool DependsOn( const std::string& lib1, const std::string& lib2,
|
||||||
|
const DependencyMap& dep_map,
|
||||||
|
std::set<std::string>& visited ) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<cmCustomCommand> m_CustomCommands;
|
std::vector<cmCustomCommand> m_CustomCommands;
|
||||||
std::vector<std::string> m_SourceLists;
|
std::vector<std::string> m_SourceLists;
|
||||||
TargetType m_TargetType;
|
TargetType m_TargetType;
|
||||||
std::vector<cmSourceFile*> m_SourceFiles;
|
std::vector<cmSourceFile*> m_SourceFiles;
|
||||||
LinkLibraries m_LinkLibraries;
|
LinkLibraries m_LinkLibraries;
|
||||||
|
std::vector<std::string> m_LinkDirectories;
|
||||||
bool m_InAll;
|
bool m_InAll;
|
||||||
std::string m_InstallPath;
|
std::string m_InstallPath;
|
||||||
std::set<std::string> m_Utilities;
|
std::set<std::string> m_Utilities;
|
||||||
|
|
|
@ -480,8 +480,8 @@ void cmUnixMakefileGenerator::OutputLinkLibraries(std::ostream& fout,
|
||||||
|
|
||||||
// collect all the flags needed for linking libraries
|
// collect all the flags needed for linking libraries
|
||||||
std::string linkLibs;
|
std::string linkLibs;
|
||||||
std::vector<std::string>& libdirs = m_Makefile->GetLinkDirectories();
|
const std::vector<std::string>& libdirs = tgt.GetLinkDirectories();
|
||||||
for(std::vector<std::string>::iterator libDir = libdirs.begin();
|
for(std::vector<std::string>::const_iterator libDir = libdirs.begin();
|
||||||
libDir != libdirs.end(); ++libDir)
|
libDir != libdirs.end(); ++libDir)
|
||||||
{
|
{
|
||||||
std::string libpath = this->ConvertToOutputPath(libDir->c_str());
|
std::string libpath = this->ConvertToOutputPath(libDir->c_str());
|
||||||
|
@ -564,11 +564,6 @@ void cmUnixMakefileGenerator::OutputLinkLibraries(std::ostream& fout,
|
||||||
|
|
||||||
linkLibs += librariesLinked;
|
linkLibs += librariesLinked;
|
||||||
|
|
||||||
if(!targetLibrary)
|
|
||||||
{
|
|
||||||
// For executables, add these a second time so order does not matter
|
|
||||||
linkLibs += librariesLinked;
|
|
||||||
}
|
|
||||||
fout << linkLibs;
|
fout << linkLibs;
|
||||||
|
|
||||||
if(outputRuntime && runtimeDirs.size()>0)
|
if(outputRuntime && runtimeDirs.size()>0)
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
PROJECT( Dependency )
|
||||||
|
|
||||||
|
SET( LIBRARY_OUTPUT_PATH ${Dependency_BINARY_DIR}/Lib )
|
||||||
|
SET( CMAKE_ANALYZE_LIB_DEPENDS "ON" )
|
||||||
|
|
||||||
|
# There is one executable that depends on eight libraries. The
|
||||||
|
# system has the following dependency graph:
|
||||||
|
#
|
||||||
|
# +----------- NoDepC <---- EXECUTABLE
|
||||||
|
# | | |
|
||||||
|
# V | |
|
||||||
|
# | |
|
||||||
|
# NoDepA <----- NoDepB <-------+ |
|
||||||
|
# |
|
||||||
|
# ^ |
|
||||||
|
# | |
|
||||||
|
# One <------ Four -----> Two <----- Five <---+
|
||||||
|
# |
|
||||||
|
# ^ ^ |
|
||||||
|
# | | |
|
||||||
|
# +--------- Three <-------+
|
||||||
|
#
|
||||||
|
# NoDepA:
|
||||||
|
# NoDepB: NoDepA
|
||||||
|
# NoDepC: NoDepA
|
||||||
|
# One:
|
||||||
|
# Two: Three
|
||||||
|
# Three: One Four
|
||||||
|
# Four: One Two A
|
||||||
|
# Five: Two
|
||||||
|
# Exec: NoDepB NoDepC Five
|
||||||
|
#
|
||||||
|
# The libraries One,...,Five have their dependencies explicitly
|
||||||
|
# encoded. The libraries NoDepA,...,NoDepC do not.
|
||||||
|
|
||||||
|
SUBDIRS( NoDepA NoDepB NoDepC )
|
||||||
|
SUBDIRS( One Two Three Four Five )
|
||||||
|
SUBDIRS( Exec )
|
|
@ -0,0 +1,6 @@
|
||||||
|
ADD_EXECUTABLE( exec ExecMain.c )
|
||||||
|
|
||||||
|
# This executable directly depends on NoDepB, NoDepC and Five. However,
|
||||||
|
# since NoDepB and NoDepC do not have explicit dependency information,
|
||||||
|
# and they depend on NoDepA, we have to manually specify that dependency.
|
||||||
|
LINK_LIBRARIES( NoDepB NoDepC NoDepA Five )
|
|
@ -0,0 +1,16 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void NoDepBFunction();
|
||||||
|
void NoDepCFunction();
|
||||||
|
void FiveFunction();
|
||||||
|
|
||||||
|
int main( )
|
||||||
|
{
|
||||||
|
FiveFunction();
|
||||||
|
NoDepBFunction();
|
||||||
|
NoDepCFunction();
|
||||||
|
|
||||||
|
printf("Dependency test executable ran successfully.\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
ADD_LIBRARY( Five FiveSrc.c )
|
||||||
|
TARGET_LINK_LIBRARIES( Five Two )
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
void TwoFunction();
|
||||||
|
|
||||||
|
void FiveFunction()
|
||||||
|
{
|
||||||
|
TwoFunction();
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
ADD_LIBRARY( Four FourSrc.c )
|
||||||
|
TARGET_LINK_LIBRARIES( Four One Two NoDepA )
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
void NoDepAFunction();
|
||||||
|
void OneFunction();
|
||||||
|
void TwoFunction();
|
||||||
|
|
||||||
|
void FourFunction()
|
||||||
|
{
|
||||||
|
static int count = 0;
|
||||||
|
if( count == 0 ) {
|
||||||
|
++count;
|
||||||
|
TwoFunction();
|
||||||
|
}
|
||||||
|
OneFunction();
|
||||||
|
NoDepAFunction();
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
ADD_LIBRARY( NoDepA NoDepASrc.c )
|
|
@ -0,0 +1,3 @@
|
||||||
|
void NoDepAFunction()
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
ADD_LIBRARY( NoDepB NoDepBSrc.c )
|
||||||
|
# This library depends on NoDepA, but the
|
||||||
|
# dependency is not explicitly specified.
|
|
@ -0,0 +1,6 @@
|
||||||
|
void NoDepAFunction();
|
||||||
|
|
||||||
|
void NoDepBFunction()
|
||||||
|
{
|
||||||
|
NoDepAFunction();
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
ADD_LIBRARY( NoDepC NoDepCSrc.c )
|
||||||
|
# This library depends on NoDepA, but the
|
||||||
|
# dependency is not explicitly specified.
|
|
@ -0,0 +1,6 @@
|
||||||
|
void NoDepAFunction();
|
||||||
|
|
||||||
|
void NoDepCFunction()
|
||||||
|
{
|
||||||
|
NoDepAFunction();
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
ADD_LIBRARY( One OneSrc.c )
|
||||||
|
# This library has no dependencies
|
||||||
|
TARGET_LINK_LIBRARIES( One "" )
|
|
@ -0,0 +1,3 @@
|
||||||
|
void OneFunction()
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
ADD_LIBRARY( Three ThreeSrc.c )
|
||||||
|
TARGET_LINK_LIBRARIES( Three One Four )
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
void OneFunction();
|
||||||
|
void FourFunction();
|
||||||
|
|
||||||
|
void ThreeFunction()
|
||||||
|
{
|
||||||
|
static int count = 0;
|
||||||
|
if( count == 0 ) {
|
||||||
|
++count;
|
||||||
|
FourFunction();
|
||||||
|
}
|
||||||
|
OneFunction();
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
ADD_LIBRARY( Two TwoSrc.c )
|
||||||
|
TARGET_LINK_LIBRARIES( Two Three )
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
void ThreeFunction();
|
||||||
|
|
||||||
|
void TwoFunction()
|
||||||
|
{
|
||||||
|
static int count = 0;
|
||||||
|
if( count == 0 ) {
|
||||||
|
++count;
|
||||||
|
ThreeFunction();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue