summaryrefslogtreecommitdiffstats
path: root/Source/cmTarget.cxx
diff options
context:
space:
mode:
authorAmitha Perera <perera@cs.rpi.edu>2002-05-01 18:00:21 (GMT)
committerAmitha Perera <perera@cs.rpi.edu>2002-05-01 18:00:21 (GMT)
commit1f8df8585ef36aa980d13a0cb6646de399bceff9 (patch)
tree3106ab0d6e942aa15c783425316235c75c790e8b /Source/cmTarget.cxx
parentd53458de9ac298344a87d6703de78991d6770832 (diff)
downloadCMake-1f8df8585ef36aa980d13a0cb6646de399bceff9.zip
CMake-1f8df8585ef36aa980d13a0cb6646de399bceff9.tar.gz
CMake-1f8df8585ef36aa980d13a0cb6646de399bceff9.tar.bz2
ENH: Add library dependency analysis.
Diffstat (limited to 'Source/cmTarget.cxx')
-rw-r--r--Source/cmTarget.cxx309
1 files changed, 303 insertions, 6 deletions
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index cd0a96a..8410f0e 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -17,6 +17,10 @@
#include "cmTarget.h"
#include "cmMakefile.h"
+#include <map>
+#include <set>
+
+
void cmTarget::GenerateSourceFilesFromSourceLists( cmMakefile &mf)
{
// this is only done for non install targets
@@ -59,20 +63,52 @@ void cmTarget::GenerateSourceFilesFromSourceLists( cmMakefile &mf)
LinkLibraries::iterator p = m_LinkLibraries.begin();
for (;p != m_LinkLibraries.end(); ++p)
{
- mf.ExpandVariablesInString(p->first);
+ mf.ExpandVariablesInString(p->first);
}
}
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() );
+}
+
+void cmTarget::MergeDirectories(const std::vector<std::string> &ld)
+{
+ m_LinkDirectories.insert( m_LinkDirectories.end(), ld.begin(), ld.end() );
+}
+
- LinkLibraries::const_iterator p = ll.begin();
- for (;p != ll.end(); ++p)
+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
@@ -87,3 +123,264 @@ bool cmTarget::HasCxx() const
}
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;
+}