diff options
author | Amitha Perera <perera@cs.rpi.edu> | 2002-05-01 18:00:21 (GMT) |
---|---|---|
committer | Amitha Perera <perera@cs.rpi.edu> | 2002-05-01 18:00:21 (GMT) |
commit | 1f8df8585ef36aa980d13a0cb6646de399bceff9 (patch) | |
tree | 3106ab0d6e942aa15c783425316235c75c790e8b /Source/cmTarget.cxx | |
parent | d53458de9ac298344a87d6703de78991d6770832 (diff) | |
download | CMake-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.cxx | 309 |
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; +} |