/*========================================================================= 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 "cmTarget.h" #include "cmMakefile.h" #include "cmSourceFile.h" #include "cmGlobalGenerator.h" #include #include #include #include // required for atof void cmTarget::SetType(TargetType type, const char* name) { m_Name = name; // only add dependency information for library targets m_TargetType = type; if(m_TargetType >= STATIC_LIBRARY && m_TargetType <= MODULE_LIBRARY) { m_RecordDependencies = true; } else { m_RecordDependencies = false; } } void cmTarget::TraceVSDependencies(std::string projFile, cmMakefile *makefile) { // get the classes from the source lists then add them to the groups std::vector & classes = this->GetSourceFiles(); // use a deck to keep track of processed source files std::queue srcFilesToProcess; std::set srcFilesQueued; std::string name; std::vector newClasses; for(std::vector::const_iterator i = classes.begin(); i != classes.end(); ++i) { name = (*i)->GetSourceName(); if ((*i)->GetSourceExtension() != "rule") { name += "."; name += (*i)->GetSourceExtension(); } srcFilesToProcess.push(name); srcFilesQueued.insert(name); // does this sourcefile have object depends on it? // If so then add them as well const char* additionalDeps = (*i)->GetProperty("OBJECT_DEPENDS"); if (additionalDeps) { std::vector depends; cmSystemTools::ExpandListArgument(additionalDeps, depends); for(std::vector::iterator id = depends.begin(); id != depends.end(); ++id) { // if there is a custom rule to generate that dependency // then add it to the list cmSourceFile* outsf = makefile->GetSourceFileWithOutput(id->c_str()); // if a source file was found then add it if (outsf && (std::find(classes.begin(),classes.end(),outsf) == classes.end()) && (std::find(newClasses.begin(),newClasses.end(),outsf) == newClasses.end())) { // then add the source to this target and add it to the queue newClasses.push_back(outsf); name = outsf->GetSourceName(); if (outsf->GetSourceExtension() != "rule") { name += "."; name += outsf->GetSourceExtension(); } std::string temp = cmSystemTools::GetFilenamePath(outsf->GetFullPath()); temp += "/"; temp += name; // if it hasn't been processed if (srcFilesQueued.find(temp) == srcFilesQueued.end()) { srcFilesToProcess.push(temp); srcFilesQueued.insert(temp); } } } } } for(std::vector::const_iterator i = newClasses.begin(); i != newClasses.end(); ++i) { classes.push_back(*i); } // add in the project file itself srcFilesToProcess.push(projFile); srcFilesQueued.insert(projFile); // add in the library depends for custom targets if (this->GetType() == cmTarget::UTILITY) { for (std::vector::iterator ic = this->GetPostBuildCommands().begin(); ic != this->GetPostBuildCommands().end(); ++ic) { cmCustomCommand &c = *ic; for (std::vector::iterator i = c.GetDepends().begin(); i != c.GetDepends().end(); ++i) { srcFilesToProcess.push(*i); srcFilesQueued.insert(*i); } } } while (!srcFilesToProcess.empty()) { // is this source the output of a custom command cmSourceFile* outsf = makefile->GetSourceFileWithOutput(srcFilesToProcess.front().c_str()); if (outsf) { // is it not already in the target? if (std::find(classes.begin(),classes.end(),outsf) == classes.end()) { // then add the source to this target and add it to the queue classes.push_back(outsf); name = outsf->GetSourceName(); if (outsf->GetSourceExtension() != "rule") { name += "."; name += outsf->GetSourceExtension(); } std::string temp = cmSystemTools::GetFilenamePath(outsf->GetFullPath()); temp += "/"; temp += name; // if it hasn't been processed if (srcFilesQueued.find(temp) == srcFilesQueued.end()) { srcFilesToProcess.push(temp); srcFilesQueued.insert(temp); } } // add its dependencies to the list to check unsigned int i; for (i = 0; i < outsf->GetCustomCommand()->GetDepends().size(); ++i) { std::string dep = cmSystemTools::GetFilenameName( outsf->GetCustomCommand()->GetDepends()[i]); if (cmSystemTools::GetFilenameLastExtension(dep) == ".exe") { dep = cmSystemTools::GetFilenameWithoutLastExtension(dep); } // watch for target dependencies, std::string libPath = dep + "_CMAKE_PATH"; const char* cacheValue = makefile->GetDefinition(libPath.c_str()); if (cacheValue && *cacheValue) { // add the depend as a utility on the target this->AddUtility(dep.c_str()); } else { if (srcFilesQueued.find(outsf->GetCustomCommand()->GetDepends()[i]) == srcFilesQueued.end()) { srcFilesToProcess.push(outsf->GetCustomCommand()->GetDepends()[i]); srcFilesQueued.insert(outsf->GetCustomCommand()->GetDepends()[i]); } } } } // finished with this SF move to the next srcFilesToProcess.pop(); } } void cmTarget::GenerateSourceFilesFromSourceLists( cmMakefile &mf) { // this is only done for non install targets if ((this->m_TargetType == cmTarget::INSTALL_FILES) || (this->m_TargetType == cmTarget::INSTALL_PROGRAMS)) { return; } // for each src lists add the classes for (std::vector::const_iterator s = m_SourceLists.begin(); s != m_SourceLists.end(); ++s) { int done = 0; // replace any variables std::string temps = *s; mf.ExpandVariablesInString(temps); // Next if one wasn't found then assume it is a single class // check to see if it is an existing source file if (!done && mf.GetSource(temps.c_str())) { m_SourceFiles.push_back(mf.GetSource(temps.c_str())); done = 1; } // if it wasn't a source file listed with the makefile // see if it is a variable. This is for old CMake 1.2 compatability // where a source list would be passed into here, by making it // a vector we need to possibly lookup the variable to maintain // CMake 1.2 compatability. const char* versionValue = mf.GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION"); if (!done) { if (!versionValue || atof(versionValue) <= 1.2) { const char* varValue = mf.GetDefinition(temps.c_str()); // if the definition exists if (varValue) { std::vector args; cmSystemTools::ExpandListArgument(varValue, args); unsigned int i; for (i = 0; i < args.size(); ++i) { if (mf.GetSource(args[i].c_str())) { m_SourceFiles.push_back(mf.GetSource(args[i].c_str())); } else { cmSourceFile file; file.SetProperty("ABSTRACT","0"); file.SetName(args[i].c_str(), mf.GetCurrentDirectory(), mf.GetSourceExtensions(), mf.GetHeaderExtensions()); m_SourceFiles.push_back(mf.AddSource(file)); } } done = 1; } } } // if we still are not done, try to create the SourceFile structure if (!done) { cmSourceFile file; file.SetProperty("ABSTRACT","0"); file.SetName(temps.c_str(), mf.GetCurrentDirectory(), mf.GetSourceExtensions(), mf.GetHeaderExtensions()); m_SourceFiles.push_back(mf.AddSource(file)); } } // expand any link library variables whle we are at it LinkLibraries::iterator p = m_LinkLibraries.begin(); for (;p != m_LinkLibraries.end(); ++p) { mf.ExpandVariablesInString(p->first); } } void cmTarget::MergeLinkLibraries( cmMakefile& mf, const char *selfname, const LinkLibraries& libs ) { // Only add on libraries we haven't added on before. // Assumption: the global link libraries could only grow, never shrink LinkLibraries::const_iterator i = libs.begin(); i += m_PrevLinkedLibraries.size(); for( ; i != libs.end(); ++i ) { // We call this so that the dependencies get written to the cache this->AddLinkLibrary( mf, selfname, i->first.c_str(), i->second ); } m_PrevLinkedLibraries = libs; } void cmTarget::AddLinkDirectory(const char* d) { // Make sure we don't add unnecessary search directories. if( std::find( m_LinkDirectories.begin(), m_LinkDirectories.end(), d ) == m_LinkDirectories.end() ) m_LinkDirectories.push_back( d ); } void cmTarget::ClearDependencyInformation( cmMakefile& mf, const char* target ) { // Clear the dependencies. The cache variable must exist iff we are // recording dependency information for this target. std::string depname = target; depname += "_LIB_DEPENDS"; if (m_RecordDependencies) { mf.AddCacheDefinition(depname.c_str(), "", "Dependencies for target", cmCacheManager::STATIC); } else { if (mf.GetDefinition( depname.c_str() )) { std::string message = "Target "; message += target; message += " has dependency information when it shouldn't.\n"; message += "Your cache is probably stale. Please remove the entry\n "; message += depname; message += "\nfrom the cache."; cmSystemTools::Error( message.c_str() ); } } } void cmTarget::AddLinkLibrary(const std::string& lib, LinkLibraryType llt) { m_LinkLibraries.push_back( std::pair(lib,llt) ); } void cmTarget::AddLinkLibrary(cmMakefile& mf, const char *target, const char* lib, LinkLibraryType llt) { // Never add a self dependency, even if the user asks for it. if(strcmp( target, lib ) == 0) { return; } m_LinkLibraries.push_back( std::pair(lib,llt) ); if(llt != cmTarget::GENERAL) { // Store the library's link type in the cache. If it is a // conflicting type then assume it is always used. This is the // case when the user sets the cache entries for debug and // optimized versions of the library to the same value. std::string linkTypeName = lib; linkTypeName += "_LINK_TYPE"; switch(llt) { case cmTarget::DEBUG: { const char* def = mf.GetDefinition(linkTypeName.c_str()); if(!def || strcmp(def, "debug") == 0) { mf.AddCacheDefinition(linkTypeName.c_str(), "debug", "Library is used for debug links only", cmCacheManager::STATIC); } else { mf.AddCacheDefinition(linkTypeName.c_str(), "general", "Library is used for both debug and optimized links", cmCacheManager::STATIC); } } break; case cmTarget::OPTIMIZED: { const char* def = mf.GetDefinition(linkTypeName.c_str()); if(!def || strcmp(def, "optimized") == 0) { mf.AddCacheDefinition(linkTypeName.c_str(), "optimized", "Library is used for debug links only", cmCacheManager::STATIC); } else { mf.AddCacheDefinition(linkTypeName.c_str(), "general", "Library is used for both debug and optimized links", cmCacheManager::STATIC); } } break; case cmTarget::GENERAL: break; } } // 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. // We shouldn't remove duplicates here because external libraries // may be purposefully duplicated to handle recursive dependencies, // and we removing one instance will break the link line. Duplicates // will be appropriately eliminated at emit time. if(m_RecordDependencies) { std::string targetEntry = target; targetEntry += "_LIB_DEPENDS"; std::string dependencies; const char* old_val = mf.GetDefinition( targetEntry.c_str() ); if( old_val ) { dependencies += old_val; } dependencies += lib; dependencies += ";"; mf.AddCacheDefinition( targetEntry.c_str(), dependencies.c_str(), "Dependencies for the target", cmCacheManager::STATIC ); } } void cmTarget::AnalyzeLibDependencies( const cmMakefile& mf ) { // There are two key parts of the dependency analysis: (1) // determining the libraries in the link line, and (2) constructing // the dependency graph for those libraries. // // The latter is done using the cache entries that record the // dependencies of each library. // // The former is a more thorny issue, since it is not clear how to // determine if two libraries listed on the link line refer to the a // single library or not. For example, consider the link "libraries" // /usr/lib/libtiff.so -ltiff // Is this one library or two? The solution implemented here is the // simplest (and probably the only practical) one: two libraries are // the same if their "link strings" are identical. Thus, the two // libraries above are considered distinct. This also means that for // dependency analysis to be effective, the CMake user must specify // libraries build by his project without using any linker flags or // file extensions. That is, // LINK_LIBRARIES( One Two ) // instead of // LINK_LIBRARIES( -lOne ${binarypath}/libTwo.a ) // The former is probably what most users would do, but it never // hurts to document the assumptions. :-) Therefore, in the analysis // code, the "canonical name" of a library is simply its name as // given to a LINK_LIBRARIES command. // // Also, we will leave the original link line intact; we will just add any // dependencies that were missing. // // There is a problem with recursive external libraries // (i.e. libraries with no dependency information that are // recursively dependent). We must make sure that the we emit one of // the libraries twice to satisfy the recursion, but we shouldn't // emit it more times than necessary. In particular, we must make // sure that handling this improbable case doesn't cost us when // dealing with the common case of non-recursive libraries. The // solution is to assume that the recursion is satisfied at one node // of the dependency tree. To illustrate, assume libA and libB are // extrenal and mutually dependent. Suppose libX depends on // libA, and libY on libA and libX. Then // TARGET_LINK_LIBRARIES( Y X A B A ) // TARGET_LINK_LIBRARIES( X A B A ) // TARGET_LINK_LIBRARIES( Exec Y ) // would result in "-lY -lX -lA -lB -lA". This is the correct way to // specify the dependencies, since the mutual dependency of A and B // is resolved *every time libA is specified*. // // Something like // TARGET_LINK_LIBRARIES( Y X A B A ) // TARGET_LINK_LIBRARIES( X A B ) // TARGET_LINK_LIBRARIES( Exec Y ) // would result in "-lY -lX -lA -lB", and the mutual dependency // information is lost. This is because in some case (Y), the mutual // dependency of A and B is listed, while in another other case (X), // it is not. Depending on which line actually emits A, the mutual // dependency may or may not be on the final link line. We can't // handle this pathalogical case cleanly without emitting extra // libraries for the normal cases. Besides, the dependency // information for X is wrong anyway: if we build an executable // depending on X alone, we would not have the mutual dependency on // A and B resolved. // // IMPROVEMENTS: // -- The current algorithm will not always pick the "optimal" link line // when recursive dependencies are present. It will instead break the // cycles at an aribtrary point. The majority of projects won't have // cyclic dependencies, so this is probably not a big deal. Note that // the link line is always correct, just not necessary optimal. typedef std::vector< std::string > LinkLine; // The dependency map. DependencyMap dep_map; // 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); // 1. Build the dependency graph // for(LinkLibraries::reverse_iterator lib = m_LinkLibraries.rbegin(); lib != m_LinkLibraries.rend(); ++lib) { this->GatherDependencies( mf, lib->first, dep_map ); } // 2. Remove any dependencies that are already satisfied in the original // link line. // for(LinkLibraries::iterator lib = m_LinkLibraries.begin(); lib != m_LinkLibraries.end(); ++lib) { for( LinkLibraries::iterator lib2 = lib; lib2 != m_LinkLibraries.end(); ++lib2) { DeleteDependency( dep_map, lib->first, lib2->first ); } } // 3. Create the new link line by simply emitting any dependencies that are // missing. Start from the back and keep adding. // std::set done, visited; std::vector newLinkLibraries; for(LinkLibraries::reverse_iterator lib = m_LinkLibraries.rbegin(); lib != m_LinkLibraries.rend(); ++lib) { // skip zero size library entries, this may happen // if a variable expands to nothing. if (lib->first.size() != 0) { Emit( lib->first, dep_map, done, visited, newLinkLibraries ); } } // 4. Add the new libraries to the link line. // for( std::vector::reverse_iterator k = newLinkLibraries.rbegin(); k != newLinkLibraries.rend(); ++k ) { if( addLibDirs ) { // who the hell knows what this is, I think that K contains the // name of a library but ... Ken // k contains the same stuff that are on the LINK_LIBRARIES // commands. Normally, they would just be library names. -- Amitha. std::string libPathStr = *k + "_CMAKE_PATH"; const char* libpath = mf.GetDefinition( libPathStr.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); } } } std::string linkType = *k; linkType += "_LINK_TYPE"; cmTarget::LinkLibraryType llt = cmTarget::GENERAL; const char* linkTypeString = mf.GetDefinition( linkType.c_str() ); if(linkTypeString) { if(strcmp(linkTypeString, "debug") == 0) { llt = cmTarget::DEBUG; } if(strcmp(linkTypeString, "optimized") == 0) { llt = cmTarget::OPTIMIZED; } } m_LinkLibraries.push_back( std::make_pair(*k,llt) ); } } void cmTarget::InsertDependency( DependencyMap& depMap, const cmStdString& lib, const cmStdString& dep ) const { depMap[lib].push_back(dep); } void cmTarget::DeleteDependency( DependencyMap& depMap, const cmStdString& lib, const cmStdString& dep ) const { // Make sure there is an entry in the map for lib. If so, delete all // dependencies to dep. There may be repeated entries because of // external libraries that are specified multiple times. DependencyMap::iterator map_itr = depMap.find( lib ); if( map_itr != depMap.end() ) { DependencyList& depList = map_itr->second; DependencyList::iterator itr; while( (itr = std::find(depList.begin(), depList.end(), dep)) != depList.end() ) { depList.erase( itr ); } } } void cmTarget::Emit( const std::string& lib, const DependencyMap& dep_map, std::set& emitted, std::set& visited, std::vector& link_line ) const { // It's already been emitted if( emitted.find(lib) != emitted.end() ) return; // Emit the dependencies only if this library node hasn't been // visited before. If it has, then we have a cycle. The recursion // that got us here should take care of everything. if( visited.insert(lib).second ) { if( dep_map.find(lib) != dep_map.end() ) // does it have dependencies? { const DependencyList& dep_on = dep_map.find( lib )->second; DependencyList::const_reverse_iterator i; // To cater for recursive external libraries, we must emit // duplicates on this link line *unless* they were emitted by // some other node, in which case we assume that the recursion // was resolved then. We making the simplifying assumption that // any duplicates on a single link line are on purpose, and must // be preserved. // This variable will keep track of the libraries that were // emitted directory from the current node, and not from a // recursive call. This way, if we come across a library that // has already been emitted, we repeat it iff it has been // emitted here. std::set emitted_here; for( i = dep_on.rbegin(); i != dep_on.rend(); ++i ) { if( emitted_here.find(*i) != emitted_here.end() ) { // a repeat. Must emit. emitted.insert(*i); link_line.push_back( *i ); } else { // Emit only if no-one else has if( emitted.find(*i) == emitted.end() ) { // emit dependencies Emit( *i, dep_map, emitted, visited, link_line ); // emit self emitted.insert(*i); emitted_here.insert(*i); link_line.push_back( *i ); } } } } } } void cmTarget::GatherDependencies( const cmMakefile& mf, const std::string& lib, DependencyMap& dep_map ) { // 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 && strcmp(deps,"") != 0 ) { // 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 ) { InsertDependency( dep_map, lib, l ); GatherDependencies( mf, l, dep_map ); } start = end+1; // skip the ; end = depline.find( ";", start ); } DeleteDependency( dep_map, lib, lib); // cannot depend on itself } } void cmTarget::SetProperty(const char* prop, const char* value) { if (!prop) { return; } if (!value) { value = "NOTFOUND"; } m_Properties[prop] = value; } const char *cmTarget::GetProperty(const char* prop) const { std::map::const_iterator i = m_Properties.find(prop); if (i != m_Properties.end()) { return i->second.c_str(); } return 0; } bool cmTarget::GetPropertyAsBool(const char* prop) const { std::map::const_iterator i = m_Properties.find(prop); if (i != m_Properties.end()) { return cmSystemTools::IsOn(i->second.c_str()); } return false; } const char* cmTarget::GetLinkerLanguage(cmGlobalGenerator* gg) const { if(this->GetProperty("HAS_CXX")) { const_cast(this)->SetProperty("LINKER_LANGUAGE", "CXX"); } const char* linkerLang = this->GetProperty("LINKER_LANGUAGE"); if(linkerLang) { return linkerLang; } std::set languages; for(std::vector::const_iterator i = m_SourceFiles.begin(); i != m_SourceFiles.end(); ++i) { const char* lang = gg->GetLanguageFromExtension((*i)->GetSourceExtension().c_str()); if(lang) { languages.insert(lang); } } if(languages.size() == 0) { return 0; } if(languages.size() == 1) { const_cast(this)->SetProperty("LINKER_LANGUAGE", languages.begin()->c_str()); return this->GetProperty("LINKER_LANGUAGE"); } const char* prefLang = 0; for(std::set::const_iterator s = languages.begin(); s != languages.end(); ++s) { const char* lpref = gg->GetLinkerPreference(s->c_str()); if(lpref[0] == 'P') { if(prefLang && !(*s == prefLang)) { std::string m = "Error Target: "; m += m_Name + " Contains more than one Prefered language: "; m += *s; m += " and "; m += prefLang; m += "\nYou must set the LINKER_LANGUAGE property for this target."; cmSystemTools::Error(m.c_str()); } else { prefLang = s->c_str(); } } } if(!prefLang) { prefLang = languages.begin()->c_str(); } const_cast(this)->SetProperty("LINKER_LANGUAGE", prefLang); return this->GetProperty("LINKER_LANGUAGE"); } const char* cmTarget::GetCreateRuleVariable() { switch(this->GetType()) { case cmTarget::STATIC_LIBRARY: return "_CREATE_STATIC_LIBRARY"; break; case cmTarget::SHARED_LIBRARY: return "_CREATE_SHARED_LIBRARY"; break; case cmTarget::MODULE_LIBRARY: return "_CREATE_SHARED_MODULE"; break; case cmTarget::EXECUTABLE: return "_LINK_EXECUTABLE"; break; case cmTarget::UTILITY: case cmTarget::INSTALL_FILES: case cmTarget::INSTALL_PROGRAMS: break; } return ""; } const char* cmTarget::GetSuffixVariable() const { switch(this->GetType()) { case cmTarget::STATIC_LIBRARY: return "CMAKE_STATIC_LIBRARY_SUFFIX"; break; case cmTarget::SHARED_LIBRARY: return "CMAKE_SHARED_LIBRARY_SUFFIX"; break; case cmTarget::MODULE_LIBRARY: return "CMAKE_SHARED_MODULE_SUFFIX"; break; case cmTarget::EXECUTABLE: return cmSystemTools::GetExecutableExtension(); case cmTarget::UTILITY: case cmTarget::INSTALL_FILES: case cmTarget::INSTALL_PROGRAMS: break; } return ""; } const char* cmTarget::GetPrefixVariable() const { switch(this->GetType()) { case cmTarget::STATIC_LIBRARY: return "CMAKE_STATIC_LIBRARY_PREFIX"; break; case cmTarget::SHARED_LIBRARY: return "CMAKE_SHARED_LIBRARY_PREFIX"; break; case cmTarget::MODULE_LIBRARY: return "CMAKE_SHARED_MODULE_PREFIX"; break; case cmTarget::EXECUTABLE: return cmSystemTools::GetExecutableExtension(); case cmTarget::UTILITY: case cmTarget::INSTALL_FILES: case cmTarget::INSTALL_PROGRAMS: break; } return ""; }