diff options
author | Brad King <brad.king@kitware.com> | 2008-01-22 14:13:04 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2008-01-22 14:13:04 (GMT) |
commit | 96fd5909d9dd1ffe740230ce652d574cd01c62d5 (patch) | |
tree | e83b3ce2d5066660256a69cacb6caa7d2d95c416 /Source | |
parent | 0df9e6904cd2416336a85d6d7972ce84dcbab417 (diff) | |
download | CMake-96fd5909d9dd1ffe740230ce652d574cd01c62d5.zip CMake-96fd5909d9dd1ffe740230ce652d574cd01c62d5.tar.gz CMake-96fd5909d9dd1ffe740230ce652d574cd01c62d5.tar.bz2 |
ENH: Implement linking with paths to library files instead of -L and -l separation. See bug #3832
- This is purely an implementation improvement. No interface has changed.
- Create cmComputeLinkInformation class
- Move and re-implement logic from:
cmLocalGenerator::ComputeLinkInformation
cmOrderLinkDirectories
- Link libraries to targets with their full path (if it is known)
- Dirs specified with link_directories command still added with -L
- Make link type specific to library names without paths
(name libfoo.a without path becomes -Wl,-Bstatic -lfoo)
- Make directory ordering specific to a runtime path computation feature
(look for conflicting SONAMEs instead of library names)
- Implement proper rpath support on HP-UX and AIX.
Diffstat (limited to 'Source')
-rw-r--r-- | Source/CMakeLists.txt | 3 | ||||
-rw-r--r-- | Source/cmComputeLinkInformation.cxx | 1157 | ||||
-rw-r--r-- | Source/cmComputeLinkInformation.h | 165 | ||||
-rw-r--r-- | Source/cmDocumentVariables.cxx | 6 | ||||
-rw-r--r-- | Source/cmGlobalGenerator.cxx | 43 | ||||
-rw-r--r-- | Source/cmGlobalGenerator.h | 21 | ||||
-rw-r--r-- | Source/cmGlobalXCodeGenerator.cxx | 88 | ||||
-rw-r--r-- | Source/cmLocalGenerator.cxx | 478 | ||||
-rw-r--r-- | Source/cmLocalGenerator.h | 9 | ||||
-rw-r--r-- | Source/cmLocalVisualStudio6Generator.cxx | 27 | ||||
-rw-r--r-- | Source/cmLocalVisualStudio7Generator.cxx | 75 | ||||
-rw-r--r-- | Source/cmLocalVisualStudio7Generator.h | 7 | ||||
-rw-r--r-- | Source/cmOrderLinkDirectories.cxx | 710 | ||||
-rw-r--r-- | Source/cmOrderLinkDirectories.h | 192 | ||||
-rw-r--r-- | Source/cmTarget.cxx | 131 | ||||
-rw-r--r-- | Source/cmTarget.h | 6 |
16 files changed, 1697 insertions, 1421 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index e821f91..aacdfcb 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -87,6 +87,8 @@ SET(SRCS cmCommandArgumentLexer.cxx cmCommandArgumentParser.cxx cmCommandArgumentParserHelper.cxx + cmComputeLinkInformation.cxx + cmComputeLinkInformation.h cmCustomCommand.cxx cmCustomCommand.h cmDepends.cxx @@ -151,7 +153,6 @@ SET(SRCS cmMakefileExecutableTargetGenerator.cxx cmMakefileLibraryTargetGenerator.cxx cmMakefileUtilityTargetGenerator.cxx - cmOrderLinkDirectories.cxx cmProperty.cxx cmProperty.h cmPropertyDefinition.cxx diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx new file mode 100644 index 0000000..50f69a2 --- /dev/null +++ b/Source/cmComputeLinkInformation.cxx @@ -0,0 +1,1157 @@ +/*========================================================================= + + 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 "cmComputeLinkInformation.h" + +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmTarget.h" + +#include <algorithm> + +#include <ctype.h> + +/* +Notes about linking on various platforms: + +------------------------------------------------------------------------------ + +Linux, FreeBSD, Mac OS X, IRIX, Sun, Windows: + +Linking to libraries using the full path works fine. + +------------------------------------------------------------------------------ + +On AIX, more work is needed. + + The "-bnoipath" option is needed. From "man ld": + + Note: If you specify a shared object, or an archive file + containing a shared object, with an absolute or relative path + name, instead of with the -lName flag, the path name is + included in the import file ID string in the loader section of + the output file. You can override this behavior with the + -bnoipath option. + + noipath + + For shared objects listed on the command-line, rather than + specified with the -l flag, use a null path component when + listing the shared object in the loader section of the + output file. A null path component is always used for + shared objects specified with the -l flag. This option + does not affect the specification of a path component by + using a line beginning with #! in an import file. The + default is the ipath option. + + This prevents the full path specified on the compile line from being + compiled directly into the binary. + + By default the linker places -L paths in the embedded runtime path. + In order to implement CMake's RPATH interface correctly, we need the + -blibpath:Path option. From "man ld": + + libpath:Path + + Uses Path as the library path when writing the loader section + of the output file. Path is neither checked for validity nor + used when searching for libraries specified by the -l flag. + Path overrides any library paths generated when the -L flag is + used. + + If you do not specify any -L flags, or if you specify the + nolibpath option, the default library path information is + written in the loader section of the output file. The default + library path information is the value of the LIBPATH + environment variable if it is defined, and /usr/lib:/lib, + otherwise. + + We can pass -Wl,-blibpath:/usr/lib:/lib always to avoid the -L stuff + and not break when the user sets LIBPATH. Then if we want to add an + rpath we insert it into the option before /usr/lib. + +------------------------------------------------------------------------------ + +On HP-UX, more work is needed. There are differences between +versions. + +ld: 92453-07 linker linker ld B.10.33 990520 + + Linking with a full path works okay for static and shared libraries. + The linker seems to always put the full path to where the library + was found in the binary whether using a full path or -lfoo syntax. + Transitive link dependencies work just fine due to the full paths. + + It has the "-l:libfoo.sl" option. The +nodefaultrpath is accepted + but not documented and does not seem to do anything. There is no + +forceload option. + +ld: 92453-07 linker ld HP Itanium(R) B.12.41 IPF/IPF + + Linking with a full path works okay for static libraries. + + Linking with a full path works okay for shared libraries. However + dependent (transitive) libraries of those linked directly must be + either found with an rpath stored in the direct dependencies or + found in -L paths as if they were specified with "-l:libfoo.sl" + (really "-l:<soname>"). The search matches that of the dynamic + loader but only with -L paths. In other words, if we have an + executable that links to shared library bar which links to shared + library foo, the link line for the exe must contain + + /dir/with/bar/libbar.sl -L/dir/with/foo + + It does not matter whether the exe wants to link to foo directly or + whether /dir/with/foo/libfoo.sl is listed. The -L path must still + be present. It should match the runtime path computed for the + executable taking all directly and transitively linked libraries + into account. + + The "+nodefaultrpath" option should be used to avoid getting -L + paths in the rpath unless we add our own rpath with +b. This means + that skip-build-rpath should use this option. + + See documentation in "man ld", "man dld.so", and + http://docs.hp.com/en/B2355-90968/creatingandusinglibraries.htm + + +[no]defaultrpath + +defaultrpath is the default. Include any paths that are + specified with -L in the embedded path, unless you specify the + +b option. If you use +b, only the path list specified by +b is + in the embedded path. + + The +nodefaultrpath option removes all library paths that were + specified with the -L option from the embedded path. The linker + searches the library paths specified by the -L option at link + time. At run time, the only library paths searched are those + specified by the environment variables LD_LIBRARY_PATH and + SHLIB_PATH, library paths specified by the +b linker option, and + finally the default library paths. + + +rpathfirst + This option will cause the paths specified in RPATH (embedded + path) to be used before the paths specified in LD_LIBRARY_PATH + or SHLIB_PATH, in searching for shared libraries. This changes + the default search order of LD_LIBRARY_PATH, SHLIB_PATH, and + RPATH (embedded path). +*/ + +//---------------------------------------------------------------------------- +cmComputeLinkInformation +::cmComputeLinkInformation(cmTarget* target, const char* config) +{ + // Store context information. + this->Target = target; + this->Makefile = this->Target->GetMakefile(); + this->LocalGenerator = this->Makefile->GetLocalGenerator(); + this->GlobalGenerator = this->LocalGenerator->GetGlobalGenerator(); + + // The configuration being linked. + this->Config = config; + + // Get the language used for linking this target. + this->LinkLanguage = + this->Target->GetLinkerLanguage(this->GlobalGenerator); + + // Check whether we should use an import library for linking a target. + this->UseImportLibrary = + this->Makefile->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")?true:false; + + // On platforms without import libraries there may be a special flag + // to use when creating a plugin (module) that obtains symbols from + // the program that will load it. + this->LoaderFlag = 0; + if(!this->UseImportLibrary && + this->Target->GetType() == cmTarget::MODULE_LIBRARY) + { + std::string loader_flag_var = "CMAKE_SHARED_MODULE_LOADER_"; + loader_flag_var += this->LinkLanguage; + loader_flag_var += "_FLAG"; + this->LoaderFlag = this->Makefile->GetDefinition(loader_flag_var.c_str()); + } + + // Get options needed to link libraries. + this->LibLinkFlag = + this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FLAG"); + this->LibLinkSuffix = + this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX"); + + // Get link type information. + this->ComputeLinkTypeInfo(); + + // Setup the link item parser. + this->ComputeItemParserInfo(); + + // Setup framework support. + this->ComputeFrameworkInfo(); + + // Initial state. + this->RuntimeSearchPathComputed = false; +} + +//---------------------------------------------------------------------------- +cmComputeLinkInformation::ItemVector const& +cmComputeLinkInformation::GetItems() +{ + return this->Items; +} + +//---------------------------------------------------------------------------- +std::vector<std::string> const& cmComputeLinkInformation::GetDirectories() +{ + return this->Directories; +} + +//---------------------------------------------------------------------------- +std::vector<std::string> const& cmComputeLinkInformation::GetDepends() +{ + return this->Depends; +} + +//---------------------------------------------------------------------------- +std::vector<std::string> const& cmComputeLinkInformation::GetFrameworkPaths() +{ + return this->FrameworkPaths; +} + +//---------------------------------------------------------------------------- +bool cmComputeLinkInformation::Compute() +{ + // Skip targets that do not link. + if(!(this->Target->GetType() == cmTarget::EXECUTABLE || + this->Target->GetType() == cmTarget::SHARED_LIBRARY || + this->Target->GetType() == cmTarget::MODULE_LIBRARY || + this->Target->GetType() == cmTarget::STATIC_LIBRARY)) + { + return false; + } + + // We require a link language for the target. + if(!this->LinkLanguage) + { + cmSystemTools:: + Error("CMake can not determine linker language for target:", + this->Target->GetName()); + return false; + } + + // Compute which library configuration to link. + cmTarget::LinkLibraryType linkType = cmTarget::OPTIMIZED; + if(this->Config && cmSystemTools::UpperCase(this->Config) == "DEBUG") + { + linkType = cmTarget::DEBUG; + } + + // Get the list of libraries against which this target wants to link. + { + const cmTarget::LinkLibraryVectorType& libs = + this->Target->GetLinkLibraries(); + for(cmTarget::LinkLibraryVectorType::const_iterator li = libs.begin(); + li != libs.end(); ++li) + { + // Link to a library if it is not the same target and is meant for + // this configuration type. + if((this->Target->GetType() == cmTarget::EXECUTABLE || + li->first != this->Target->GetName()) && + (li->second == cmTarget::GENERAL || li->second == linkType)) + { + this->AddItem(li->first); + } + } + } + + // Restore the target link type so the correct system runtime + // libraries are found. + this->SetCurrentLinkType(this->StartLinkType); + + // Compute the linker search path. + this->ComputeLinkerSearchDirectories(); + + return true; +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::AddItem(std::string const& item) +{ + // Compute the proper name to use to link this library. + // TODO: Change third argument to support imported libraries. + cmTarget* tgt = this->GlobalGenerator->FindTarget(0, item.c_str(), false); + const char* config = this->Config; + bool implib = this->UseImportLibrary; + bool impexe = (tgt && + tgt->GetType() == cmTarget::EXECUTABLE && + tgt->GetPropertyAsBool("ENABLE_EXPORTS")); + if(impexe && !implib && !this->LoaderFlag) + { + // Skip linking to executables on platforms with no import + // libraries or loader flags. + return; + } + + if(tgt && (tgt->GetType() == cmTarget::STATIC_LIBRARY || + tgt->GetType() == cmTarget::SHARED_LIBRARY || + tgt->GetType() == cmTarget::MODULE_LIBRARY || + impexe)) + { + // This is a CMake target. Ask the target for its real name. + if(impexe && this->LoaderFlag) + { + // This link item is an executable that may provide symbols + // used by this target. A special flag is needed on this + // platform. Add it now. + std::string linkItem; + linkItem = this->LoaderFlag; + std::string exe = tgt->GetFullPath(config, implib); + linkItem += exe; + this->Items.push_back(Item(linkItem, true)); + this->Depends.push_back(exe); + } + else + { + // Pass the full path to the target file. + std::string lib = tgt->GetFullPath(config, implib); + this->Depends.push_back(lib); +#ifdef __APPLE__ + if(tgt->GetType() == cmTarget::SHARED_LIBRARY && + tgt->GetPropertyAsBool("FRAMEWORK")) + { + // Frameworks on OS X need only the framework directory to + // link. + std::string fw = tgt->GetDirectory(config, implib); + this->AddFrameworkItem(fw); + } + else +#endif + { + this->Items.push_back(Item(lib, true)); + this->AddLibraryRuntimeInfo(lib, tgt); + } + } + } + else + { + // This is not a CMake target. Use the name given. + if(cmSystemTools::FileIsFullPath(item.c_str())) + { + if(cmSystemTools::FileIsDirectory(item.c_str())) + { + // This is a directory. + this->AddDirectoryItem(item); + } + else + { + // Use the full path given to the library file. + this->Items.push_back(Item(item, true)); + this->Depends.push_back(item); + this->AddLibraryRuntimeInfo(item); + } + } + else + { + // This is a library or option specified by the user. + this->AddUserItem(item); + } + } +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::ComputeLinkTypeInfo() +{ + // First assume we cannot do link type stuff. + this->LinkTypeEnabled = false; + + // Lookup link type selection flags. + const char* static_link_type_flag = 0; + const char* shared_link_type_flag = 0; + const char* target_type_str = 0; + switch(this->Target->GetType()) + { + case cmTarget::EXECUTABLE: target_type_str = "EXE"; break; + case cmTarget::SHARED_LIBRARY: target_type_str = "SHARED_LIBRARY"; break; + case cmTarget::MODULE_LIBRARY: target_type_str = "SHARED_MODULE"; break; + default: break; + } + if(target_type_str) + { + std::string static_link_type_flag_var = "CMAKE_"; + static_link_type_flag_var += target_type_str; + static_link_type_flag_var += "_LINK_STATIC_"; + static_link_type_flag_var += this->LinkLanguage; + static_link_type_flag_var += "_FLAGS"; + static_link_type_flag = + this->Makefile->GetDefinition(static_link_type_flag_var.c_str()); + + std::string shared_link_type_flag_var = "CMAKE_"; + shared_link_type_flag_var += target_type_str; + shared_link_type_flag_var += "_LINK_DYNAMIC_"; + shared_link_type_flag_var += this->LinkLanguage; + shared_link_type_flag_var += "_FLAGS"; + shared_link_type_flag = + this->Makefile->GetDefinition(shared_link_type_flag_var.c_str()); + } + + // We can support link type switching only if all needed flags are + // known. + if(static_link_type_flag && *static_link_type_flag && + shared_link_type_flag && *shared_link_type_flag) + { + this->LinkTypeEnabled = true; + this->StaticLinkTypeFlag = static_link_type_flag; + this->SharedLinkTypeFlag = shared_link_type_flag; + } + + // TODO: Lookup the starting link type from the target (is it being + // linked statically?). + this->StartLinkType = LinkShared; + this->CurrentLinkType = this->StartLinkType; +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::ComputeItemParserInfo() +{ + // Get possible library name prefixes. + cmMakefile* mf = this->Makefile; + this->AddLinkPrefix(mf->GetDefinition("CMAKE_STATIC_LIBRARY_PREFIX")); + this->AddLinkPrefix(mf->GetDefinition("CMAKE_SHARED_LIBRARY_PREFIX")); + + // Import library names should be matched and treated as shared + // libraries for the purposes of linking. + this->AddLinkExtension(mf->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX"), + LinkShared); + this->AddLinkExtension(mf->GetDefinition("CMAKE_STATIC_LIBRARY_SUFFIX"), + LinkStatic); + this->AddLinkExtension(mf->GetDefinition("CMAKE_SHARED_LIBRARY_SUFFIX"), + LinkShared); + this->AddLinkExtension(mf->GetDefinition("CMAKE_LINK_LIBRARY_SUFFIX"), + LinkUnknown); + if(const char* linkSuffixes = + mf->GetDefinition("CMAKE_EXTRA_LINK_EXTENSIONS")) + { + std::vector<std::string> linkSuffixVec; + cmSystemTools::ExpandListArgument(linkSuffixes, linkSuffixVec); + for(std::vector<std::string>::iterator i = linkSuffixVec.begin(); + i != linkSuffixVec.end(); ++i) + { + this->AddLinkExtension(i->c_str(), LinkUnknown); + } + } + + // Compute a regex to match link extensions. + std::string libext = this->CreateExtensionRegex(this->LinkExtensions); + + // Create regex to remove any library extension. + std::string reg("(.*)"); + reg += libext; + this->RemoveLibraryExtension.compile(reg.c_str()); + + // Create a regex to match a library name. Match index 1 will be + // the prefix if it exists and empty otherwise. Match index 2 will + // be the library name. Match index 3 will be the library + // extension. + reg = "^("; + for(std::set<cmStdString>::iterator p = this->LinkPrefixes.begin(); + p != this->LinkPrefixes.end(); ++p) + { + reg += *p; + reg += "|"; + } + reg += ")"; + reg += "([^/]*)"; + + // Create a regex to match any library name. + std::string reg_any = reg; + reg_any += libext; +#ifdef CM_COMPUTE_LINK_INFO_DEBUG + fprintf(stderr, "any regex [%s]\n", reg_any.c_str()); +#endif + this->ExtractAnyLibraryName.compile(reg_any.c_str()); + + // Create a regex to match static library names. + if(!this->StaticLinkExtensions.empty()) + { + std::string reg_static = reg; + reg_static += this->CreateExtensionRegex(this->StaticLinkExtensions); +#ifdef CM_COMPUTE_LINK_INFO_DEBUG + fprintf(stderr, "static regex [%s]\n", reg_static.c_str()); +#endif + this->ExtractStaticLibraryName.compile(reg_static.c_str()); + } + + // Create a regex to match shared library names. + if(!this->SharedLinkExtensions.empty()) + { + std::string reg_shared = reg; + reg_shared += this->CreateExtensionRegex(this->SharedLinkExtensions); +#ifdef CM_COMPUTE_LINK_INFO_DEBUG + fprintf(stderr, "shared regex [%s]\n", reg_shared.c_str()); +#endif + this->ExtractSharedLibraryName.compile(reg_shared.c_str()); + } +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::AddLinkPrefix(const char* p) +{ + if(p) + { + this->LinkPrefixes.insert(p); + } +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::AddLinkExtension(const char* e, LinkType type) +{ + if(e && *e) + { + if(type == LinkStatic) + { + this->StaticLinkExtensions.push_back(e); + } + if(type == LinkShared) + { + this->SharedLinkExtensions.push_back(e); + } + this->LinkExtensions.push_back(e); + } +} + +//---------------------------------------------------------------------------- +std::string +cmComputeLinkInformation +::CreateExtensionRegex(std::vector<std::string> const& exts) +{ + // Build a list of extension choices. + std::string libext = "("; + const char* sep = ""; + for(std::vector<std::string>::const_iterator i = exts.begin(); + i != exts.end(); ++i) + { + // Separate this choice from the previous one. + libext += sep; + sep = "|"; + + // Store this extension choice with the "." escaped. + libext += "\\"; +#if defined(_WIN32) && !defined(__CYGWIN__) + libext += this->NoCaseExpression(i->c_str()); +#else + libext += *i; +#endif + } + + // Finish the list. + libext += ").*"; + return libext; +} + +//---------------------------------------------------------------------------- +std::string cmComputeLinkInformation::NoCaseExpression(const char* str) +{ + std::string ret; + const char* s = str; + while(*s) + { + if(*s == '.') + { + ret += *s; + } + else + { + ret += "["; + ret += tolower(*s); + ret += toupper(*s); + ret += "]"; + } + s++; + } + return ret; +} + +//------------------------------------------------------------------- +void cmComputeLinkInformation::SetCurrentLinkType(LinkType lt) +{ + // If we are changing the current link type add the flag to tell the + // linker about it. + if(this->CurrentLinkType != lt) + { + this->CurrentLinkType = lt; + + if(this->LinkTypeEnabled) + { + switch(this->CurrentLinkType) + { + case LinkStatic: + this->Items.push_back(Item(this->StaticLinkTypeFlag, false)); + break; + case LinkShared: + this->Items.push_back(Item(this->SharedLinkTypeFlag, false)); + break; + default: + break; + } + } + } +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::AddUserItem(std::string const& item) +{ + // This is called to handle a link item that does not match a CMake + // target and is not a full path. We check here if it looks like a + // library file name to automatically request the proper link type + // from the linker. For example: + // + // foo ==> -lfoo + // libfoo.a ==> -Wl,-Bstatic -lfoo + std::string lib; + + // Parse out the prefix, base, and suffix components of the + // library name. If the name matches that of a shared or static + // library then set the link type accordingly. + // + // Search for shared library names first because some platforms + // have shared libraries with names that match the static library + // pattern. For example cygwin and msys use the convention + // libfoo.dll.a for import libraries and libfoo.a for static + // libraries. On AIX a library with the name libfoo.a can be + // shared! + if(this->ExtractSharedLibraryName.find(item)) + { + // This matches a shared library file name. +#ifdef CM_COMPUTE_LINK_INFO_DEBUG + fprintf(stderr, "shared regex matched [%s] [%s] [%s]\n", + this->ExtractSharedLibraryName.match(1).c_str(), + this->ExtractSharedLibraryName.match(2).c_str(), + this->ExtractSharedLibraryName.match(3).c_str()); +#endif + // Set the link type to shared. + this->SetCurrentLinkType(LinkShared); + + // Use just the library name so the linker will search. + lib = this->ExtractSharedLibraryName.match(2); + } + else if(this->ExtractStaticLibraryName.find(item)) + { + // This matches a static library file name. +#ifdef CM_COMPUTE_LINK_INFO_DEBUG + fprintf(stderr, "static regex matched [%s] [%s] [%s]\n", + this->ExtractStaticLibraryName.match(1).c_str(), + this->ExtractStaticLibraryName.match(2).c_str(), + this->ExtractStaticLibraryName.match(3).c_str()); +#endif + // Set the link type to static. + this->SetCurrentLinkType(LinkStatic); + + // Use just the library name so the linker will search. + lib = this->ExtractStaticLibraryName.match(2); + } + else if(this->ExtractAnyLibraryName.find(item)) + { + // This matches a library file name. +#ifdef CM_COMPUTE_LINK_INFO_DEBUG + fprintf(stderr, "any regex matched [%s] [%s] [%s]\n", + this->ExtractAnyLibraryName.match(1).c_str(), + this->ExtractAnyLibraryName.match(2).c_str(), + this->ExtractAnyLibraryName.match(3).c_str()); +#endif + // Restore the target link type since this item does not specify + // one. + this->SetCurrentLinkType(this->StartLinkType); + + // Use just the library name so the linker will search. + lib = this->ExtractAnyLibraryName.match(2); + } + else if(item[0] == '-' || item[0] == '$' || item[0] == '`') + { + // This is a linker option provided by the user. Restore the + // target link type since this item does not specify one. + this->SetCurrentLinkType(this->StartLinkType); + + // Use the item verbatim. + this->Items.push_back(Item(item, false)); + return; + } + else + { + // This is a name specified by the user. We must ask the linker + // to search for a library with this name. Restore the target + // link type since this item does not specify one. + this->SetCurrentLinkType(this->StartLinkType); + lib = item; + } + + // Create an option to ask the linker to search for the library. + std::string out = this->LibLinkFlag; + out += lib; + out += this->LibLinkSuffix; + this->Items.push_back(Item(out, false)); + + // Here we could try to find the library the linker will find and + // add a runtime information entry for it. It would probably not be + // reliable and we want to encourage use of full paths for library + // specification. +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::AddFrameworkItem(std::string const& item) +{ + // Try to separate the framework name and path. + if(!this->SplitFramework.find(item.c_str())) + { + cmOStringStream e; + e << "Could not parse framework path \"" << item << "\" " + << "linked by target " << this->Target->GetName() << "."; + cmSystemTools::Error(e.str().c_str()); + return; + } + + // Add the directory portion to the framework search path. + this->AddFrameworkPath(this->SplitFramework.match(1)); + + // Add the item using the -framework option. + std::string fw = "-framework "; + fw += this->SplitFramework.match(2); + this->Items.push_back(Item(fw, false)); +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::AddDirectoryItem(std::string const& item) +{ +#ifdef __APPLE__ + if(cmSystemTools::IsPathToFramework(item.c_str())) + { + this->AddFrameworkItem(item); + } + else +#endif + { + this->DropDirectoryItem(item); + } +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::DropDirectoryItem(std::string const& item) +{ + // A full path to a directory was found as a link item. Warn the + // user. + cmOStringStream e; + e << "WARNING: Target \"" << this->Target->GetName() + << "\" requests linking to directory \"" << item << "\". " + << "Targets may link only to libraries. " + << "CMake is dropping the item."; + cmSystemTools::Message(e.str().c_str()); +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::ComputeFrameworkInfo() +{ + // Avoid adding system framework paths. See "man ld" on OS X. + this->FrameworkPathsEmmitted.insert("/Library/Frameworks"); + this->FrameworkPathsEmmitted.insert("/Network/Library/Frameworks"); + this->FrameworkPathsEmmitted.insert("/System/Library/Frameworks"); + + // Regular expression to extract a framework path and name. + this->SplitFramework.compile("(.*)/(.*)\\.framework$"); +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::AddFrameworkPath(std::string const& p) +{ + if(this->FrameworkPathsEmmitted.insert(p).second) + { + this->FrameworkPaths.push_back(p); + } +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::ComputeLinkerSearchDirectories() +{ + // Some search paths should never be emitted. + this->DirectoriesEmmitted.insert(""); + if(const char* implicitLinks = + (this->Makefile->GetDefinition + ("CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES"))) + { + std::vector<std::string> implicitLinkVec; + cmSystemTools::ExpandListArgument(implicitLinks, implicitLinkVec); + for(std::vector<std::string>::const_iterator + i = implicitLinkVec.begin(); + i != implicitLinkVec.end(); ++i) + { + this->DirectoriesEmmitted.insert(*i); + } + } + + // Check if we need to include the runtime search path at link time. + std::string var = "CMAKE_SHARED_LIBRARY_LINK_"; + var += this->LinkLanguage; + var += "_WITH_RUNTIME_PATH"; + if(this->Makefile->IsOn(var.c_str())) + { + // This platform requires the runtime library path for shared + // libraries to be specified at link time as -L paths. It needs + // them so that transitive dependencies of the libraries linked + // may be found by the linker. + this->AddLinkerSearchDirectories(this->GetRuntimeSearchPath()); + } + + // Get the search path entries requested by the user. + this->AddLinkerSearchDirectories(this->Target->GetLinkDirectories()); +} + +//---------------------------------------------------------------------------- +void +cmComputeLinkInformation +::AddLinkerSearchDirectories(std::vector<std::string> const& dirs) +{ + for(std::vector<std::string>::const_iterator i = dirs.begin(); + i != dirs.end(); ++i) + { + if(this->DirectoriesEmmitted.insert(*i).second) + { + this->Directories.push_back(*i); + } + } +} + +//---------------------------------------------------------------------------- +std::vector<std::string> const& +cmComputeLinkInformation::GetRuntimeSearchPath() +{ + if(!this->RuntimeSearchPathComputed) + { + this->RuntimeSearchPathComputed = true; + this->CollectRuntimeDirectories(); + this->FindConflictingLibraries(); + this->OrderRuntimeSearchPath(); + } + return this->RuntimeSearchPath; +} + +//============================================================================ +// Directory ordering computation. +// - Useful to compute a safe runtime library path order +// - Need runtime path for supporting INSTALL_RPATH_USE_LINK_PATH +// - Need runtime path at link time to pickup transitive link dependencies +// for shared libraries (in future when we do not always add them). + +//---------------------------------------------------------------------------- +void +cmComputeLinkInformation::AddLibraryRuntimeInfo(std::string const& fullPath, + cmTarget* target) +{ + // Skip targets that are not shared libraries (modules cannot be linked). + if(target->GetType() != cmTarget::SHARED_LIBRARY) + { + return; + } + + // Try to get the soname of the library. Only files with this name + // could possibly conflict. + std::string soName; + const char* soname = 0; + if(!target->IsImported()) + { + std::string name; + std::string realName; + std::string impName; + std::string pdbName; + target->GetLibraryNames(name, soName, realName, impName, pdbName, + this->Config); + soname = soName.c_str(); + } + + // Add the library runtime entry. + this->AddLibraryRuntimeInfo(fullPath, soname); +} + +//---------------------------------------------------------------------------- +void +cmComputeLinkInformation::AddLibraryRuntimeInfo(std::string const& fullPath, + const char* soname) +{ + // Get the name of the library from the file name. + std::string file = cmSystemTools::GetFilenameName(fullPath); + if(!this->ExtractSharedLibraryName.find(file.c_str())) + { + // This is not the name of a shared library. + return; + } + + // Add the runtime information at most once. + if(this->LibraryRuntimeInfoEmmitted.insert(fullPath).second) + { + // Construct the runtime information entry for this library. + LibraryRuntimeEntry entry; + entry.FileName = cmSystemTools::GetFilenameName(fullPath); + entry.SOName = soname? soname : ""; + entry.Directory = cmSystemTools::GetFilenamePath(fullPath); + this->LibraryRuntimeInfo.push_back(entry); + } + else + { + // This can happen if the same library is linked multiple times. + // In that case the runtime information check need be done only + // once anyway. For shared libs we could add a check in AddItem + // to not repeat them. + } +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::CollectRuntimeDirectories() +{ + // Get all directories that should be in the runtime search path. + + // Add directories containing libraries linked with full path. + for(std::vector<LibraryRuntimeEntry>::iterator + ei = this->LibraryRuntimeInfo.begin(); + ei != this->LibraryRuntimeInfo.end(); ++ei) + { + ei->DirectoryIndex = this->AddRuntimeDirectory(ei->Directory); + } + + // Add link directories specified for the target. + std::vector<std::string> const& dirs = this->GetDirectories(); + for(std::vector<std::string>::const_iterator di = dirs.begin(); + di != dirs.end(); ++di) + { + this->AddRuntimeDirectory(*di); + } +} + +//---------------------------------------------------------------------------- +int cmComputeLinkInformation::AddRuntimeDirectory(std::string const& dir) +{ + // Add the runtime directory with a unique index. + std::map<cmStdString, int>::iterator i = + this->RuntimeDirectoryIndex.find(dir); + if(i == this->RuntimeDirectoryIndex.end()) + { + std::map<cmStdString, int>::value_type + entry(dir, static_cast<int>(this->RuntimeDirectories.size())); + i = this->RuntimeDirectoryIndex.insert(entry).first; + this->RuntimeDirectories.push_back(dir); + } + + return i->second; +} + +//---------------------------------------------------------------------------- +struct cmCLIRuntimeConflictCompare +{ + typedef std::pair<int, int> RuntimeConflictPair; + + // The conflict pair is unique based on just the directory + // (first). The second element is only used for displaying + // information about why the entry is present. + bool operator()(RuntimeConflictPair const& l, + RuntimeConflictPair const& r) + { + return l.first == r.first; + } +}; + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::FindConflictingLibraries() +{ + // Allocate the conflict graph. + this->RuntimeConflictGraph.resize(this->RuntimeDirectories.size()); + this->RuntimeDirectoryVisited.resize(this->RuntimeDirectories.size(), 0); + + // Find all runtime directories providing each library. + for(unsigned int lri = 0; lri < this->LibraryRuntimeInfo.size(); ++lri) + { + this->FindDirectoriesForLib(lri); + } + + // Clean up the conflict graph representation. + for(std::vector<RuntimeConflictList>::iterator + i = this->RuntimeConflictGraph.begin(); + i != this->RuntimeConflictGraph.end(); ++i) + { + // Sort the outgoing edges for each graph node so that the + // original order will be preserved as much as possible. + std::sort(i->begin(), i->end()); + + // Make the edge list unique so cycle detection will be reliable. + RuntimeConflictList::iterator last = + std::unique(i->begin(), i->end(), cmCLIRuntimeConflictCompare()); + i->erase(last, i->end()); + } +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::FindDirectoriesForLib(unsigned int lri) +{ + // Search through the runtime directories to find those providing + // this library. + LibraryRuntimeEntry& re = this->LibraryRuntimeInfo[lri]; + for(unsigned int i = 0; i < this->RuntimeDirectories.size(); ++i) + { + // Skip the directory that is supposed to provide the library. + if(this->RuntimeDirectories[i] == re.Directory) + { + continue; + } + + // Determine which type of check to do. + if(!re.SOName.empty()) + { + // We have the library soname. Check if it will be found. + std::string file = this->RuntimeDirectories[i]; + file += "/"; + file += re.SOName; + std::set<cmStdString> const& files = + (this->GlobalGenerator + ->GetDirectoryContent(this->RuntimeDirectories[i], false)); + if(files.find(re.SOName) != files.end() || + cmSystemTools::FileExists(file.c_str(), true)) + { + // The library will be found in this directory but this is not + // the directory named for it. Add an entry to make sure the + // desired directory comes before this one. + RuntimeConflictPair p(re.DirectoryIndex, lri); + this->RuntimeConflictGraph[i].push_back(p); + } + } + else + { + // We do not have the soname. Look for files in the directory + // that may conflict. + std::set<cmStdString> const& files = + (this->GlobalGenerator + ->GetDirectoryContent(this->RuntimeDirectories[i], true)); + + // Get the extension which should appear in the soname. + std::string ext = + cmSystemTools::GetFilenameLastExtension(re.FileName); + + // Get the set of files that might conflict. + std::string base = + cmSystemTools::GetFilenameWithoutLastExtension(re.FileName); + std::set<cmStdString>::const_iterator first = files.lower_bound(base); + ++base[base.size()-1]; + std::set<cmStdString>::const_iterator last = files.upper_bound(base); + bool found = false; + for(std::set<cmStdString>::const_iterator fi = first; + !found && fi != last; ++fi) + { + // This file name starts with the name of the library file. + // If the name also contains the extension then this is + // possibly an soname of the library. + if(fi->find(ext, base.size()) != fi->npos) + { + found = true; + } + } + + if(found) + { + // The library may be found in this directory but this is not + // the directory named for it. Add an entry to make sure the + // desired directory comes before this one. + RuntimeConflictPair p(re.DirectoryIndex, lri); + this->RuntimeConflictGraph[i].push_back(p); + } + } + } +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::OrderRuntimeSearchPath() +{ + // Allow a cycle to be diagnosed once. + this->CycleDiagnosed = false; + + // Iterate through the directories in the original order. + for(unsigned int i=0; i < this->RuntimeDirectories.size(); ++i) + { + this->VisitRuntimeDirectory(i, true); + } +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::VisitRuntimeDirectory(unsigned int i, + bool top) +{ + // Skip nodes already visited. + if(this->RuntimeDirectoryVisited[i]) + { + if(!top) + { + // We have reached a previously visited node but were not called + // to start a new section of the graph. There is a cycle. + this->DiagnoseCycle(); + } + return; + } + + // We are not visiting this node so mark it. + this->RuntimeDirectoryVisited[i] = 1; + + // Visit the neighbors of the node first. + RuntimeConflictList const& clist = this->RuntimeConflictGraph[i]; + for(RuntimeConflictList::const_iterator j = clist.begin(); + j != clist.end(); ++j) + { + this->VisitRuntimeDirectory(j->first, false); + } + + // Now that all directories required to come before this one have + // been emmitted, emit this directory. + this->RuntimeSearchPath.push_back(this->RuntimeDirectories[i]); +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::DiagnoseCycle() +{ + // Report the cycle at most once. + if(this->CycleDiagnosed) + { + return; + } + this->CycleDiagnosed = true; + + // Construct the message. + cmOStringStream e; + e << "WARNING: Cannot generate a safe runtime path for target " + << this->Target->GetName() + << " because there is a cycle in the constraint graph:\n"; + + // Clean up the conflict graph representation. + for(unsigned int i=0; i < this->RuntimeConflictGraph.size(); ++i) + { + RuntimeConflictList const& clist = this->RuntimeConflictGraph[i]; + e << "dir " << i << " is [" << this->RuntimeDirectories[i] << "]\n"; + for(RuntimeConflictList::const_iterator j = clist.begin(); + j != clist.end(); ++j) + { + e << " dir " << j->first << " must precede it due to ["; + LibraryRuntimeEntry const& re = this->LibraryRuntimeInfo[j->second]; + if(re.SOName.empty()) + { + e << re.FileName; + } + else + { + e << re.SOName; + } + e << "]\n"; + } + } + cmSystemTools::Message(e.str().c_str()); +} diff --git a/Source/cmComputeLinkInformation.h b/Source/cmComputeLinkInformation.h new file mode 100644 index 0000000..d8a1374 --- /dev/null +++ b/Source/cmComputeLinkInformation.h @@ -0,0 +1,165 @@ +/*========================================================================= + + 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. + +=========================================================================*/ +#ifndef cmComputeLinkInformation_h +#define cmComputeLinkInformation_h + +#include "cmStandardIncludes.h" + +#include <cmsys/RegularExpression.hxx> + +class cmGlobalGenerator; +class cmLocalGenerator; +class cmMakefile; +class cmTarget; + +/** \class cmComputeLinkInformation + * \brief Compute link information for a target in one configuration. + */ +class cmComputeLinkInformation +{ +public: + cmComputeLinkInformation(cmTarget* target, const char* config); + bool Compute(); + + struct Item + { + Item(): Value(), IsPath(true) {} + Item(Item const& item): Value(item.Value), IsPath(item.IsPath) {} + Item(std::string const& v, bool p): Value(v), IsPath(p) {} + std::string Value; + bool IsPath; + }; + typedef std::vector<Item> ItemVector; + ItemVector const& GetItems(); + std::vector<std::string> const& GetDirectories(); + std::vector<std::string> const& GetDepends(); + std::vector<std::string> const& GetFrameworkPaths(); + const char* GetLinkLanguage() const { return this->LinkLanguage; } + std::vector<std::string> const& GetRuntimeSearchPath(); +private: + void AddItem(std::string const& item); + + // Output information. + ItemVector Items; + std::vector<std::string> Directories; + std::vector<std::string> Depends; + std::vector<std::string> FrameworkPaths; + std::vector<std::string> RuntimeSearchPath; + + // Context information. + cmTarget* Target; + cmMakefile* Makefile; + cmLocalGenerator* LocalGenerator; + cmGlobalGenerator* GlobalGenerator; + + // Configuration information. + const char* Config; + const char* LinkLanguage; + + // System info. + bool UseImportLibrary; + const char* LoaderFlag; + std::string LibLinkFlag; + std::string LibLinkSuffix; + + // Link type adjustment. + void ComputeLinkTypeInfo(); + enum LinkType { LinkUnknown, LinkStatic, LinkShared }; + LinkType StartLinkType; + LinkType CurrentLinkType; + std::string StaticLinkTypeFlag; + std::string SharedLinkTypeFlag; + bool LinkTypeEnabled; + void SetCurrentLinkType(LinkType lt); + + // Link item parsing. + void ComputeItemParserInfo(); + std::vector<std::string> StaticLinkExtensions; + std::vector<std::string> SharedLinkExtensions; + std::vector<std::string> LinkExtensions; + std::set<cmStdString> LinkPrefixes; + cmsys::RegularExpression RemoveLibraryExtension; + cmsys::RegularExpression ExtractStaticLibraryName; + cmsys::RegularExpression ExtractSharedLibraryName; + cmsys::RegularExpression ExtractAnyLibraryName; + void AddLinkPrefix(const char* p); + void AddLinkExtension(const char* e, LinkType type); + std::string CreateExtensionRegex(std::vector<std::string> const& exts); + std::string NoCaseExpression(const char* str); + + // Handling of link items that are not targets or full file paths. + void AddUserItem(std::string const& item); + void AddDirectoryItem(std::string const& item); + void AddFrameworkItem(std::string const& item); + void DropDirectoryItem(std::string const& item); + + // Framework info. + void ComputeFrameworkInfo(); + void AddFrameworkPath(std::string const& p); + std::set<cmStdString> FrameworkPathsEmmitted; + cmsys::RegularExpression SplitFramework; + + // Linker search path computation. + void ComputeLinkerSearchDirectories(); + void AddLinkerSearchDirectories(std::vector<std::string> const& dirs); + std::set<cmStdString> DirectoriesEmmitted; + + // Runtime path computation. + struct LibraryRuntimeEntry + { + // The file name of the library. + std::string FileName; + + // The soname of the shared library if it is known. + std::string SOName; + + // The directory in which the library is supposed to be found. + std::string Directory; + + // The index assigned to the directory. + int DirectoryIndex; + }; + bool RuntimeSearchPathComputed; + std::vector<LibraryRuntimeEntry> LibraryRuntimeInfo; + std::set<cmStdString> LibraryRuntimeInfoEmmitted; + std::vector<std::string> RuntimeDirectories; + std::map<cmStdString, int> RuntimeDirectoryIndex; + std::vector<char> RuntimeDirectoryVisited; + void AddLibraryRuntimeInfo(std::string const& fullPath, cmTarget* target); + void AddLibraryRuntimeInfo(std::string const& fullPath, + const char* soname = 0); + void CollectRuntimeDirectories(); + int AddRuntimeDirectory(std::string const& dir); + void FindConflictingLibraries(); + void FindDirectoriesForLib(unsigned int lri); + void OrderRuntimeSearchPath(); + void VisitRuntimeDirectory(unsigned int i, bool top); + void DiagnoseCycle(); + bool CycleDiagnosed; + + // Adjacency-list representation of runtime path ordering graph. + // This maps from directory to those that must come *before* it. + // Each entry that must come before is a pair. The first element is + // the index of the directory that must come first. The second + // element is the index of the runtime library that added the + // constraint. + typedef std::pair<int, int> RuntimeConflictPair; + struct RuntimeConflictList: public std::vector<RuntimeConflictPair> {}; + std::vector<RuntimeConflictList> RuntimeConflictGraph; +}; + +#endif diff --git a/Source/cmDocumentVariables.cxx b/Source/cmDocumentVariables.cxx index 9fd48d9..d744145 100644 --- a/Source/cmDocumentVariables.cxx +++ b/Source/cmDocumentVariables.cxx @@ -1035,6 +1035,12 @@ void cmDocumentVariables::DefineVariables(cmake* cm) cmProperty::VARIABLE,0,0); cm->DefineProperty("CMAKE_SHARED_LIBRARY_RUNTIME_<LANG>_FLAG_SEP", cmProperty::VARIABLE,0,0); + cm->DefineProperty("CMAKE_EXECUTABLE_RUNTIME_<LANG>_FLAG", + cmProperty::VARIABLE,0,0); + cm->DefineProperty("CMAKE_EXECUTABLE_RUNTIME_<LANG>_FLAG_SEP", + cmProperty::VARIABLE,0,0); + cm->DefineProperty("CMAKE_PLATFORM_REQUIRED_RUNTIME_PATH", + cmProperty::VARIABLE,0,0); cm->DefineProperty("CMAKE_SHARED_MODULE_CREATE_<LANG>_FLAGS", cmProperty::VARIABLE,0,0); cm->DefineProperty("CMAKE_SHARED_MODULE_<LANG>_FLAGS", diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 8f8325e..448d457 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -27,6 +27,8 @@ #include "cmVersion.h" #include "cmInstallExportGenerator.h" +#include <cmsys/Directory.hxx> + #include <stdlib.h> // required for atof #include <assert.h> @@ -784,7 +786,7 @@ void cmGlobalGenerator::Generate() // Compute the manifest of main targets generated. for (i = 0; i < this->LocalGenerators.size(); ++i) { - this->LocalGenerators[i]->GenerateTargetManifest(this->TargetManifest); + this->LocalGenerators[i]->GenerateTargetManifest(); } // Create a map from local generator to the complete set of targets @@ -1880,3 +1882,42 @@ cmGlobalGenerator this->FilesReplacedDuringGenerate.end(), std::back_inserter(filenames)); } + +//---------------------------------------------------------------------------- +void cmGlobalGenerator::AddToManifest(const char* config, + std::string const& f) +{ + // Add to the main manifest for this configuration. + this->TargetManifest[config].insert(f); + + // Add to the content listing for the file's directory. + std::string dir = cmSystemTools::GetFilenamePath(f); + std::string file = cmSystemTools::GetFilenameName(f); + this->DirectoryContentMap[dir].insert(file); +} + +//---------------------------------------------------------------------------- +std::set<cmStdString> const& +cmGlobalGenerator::GetDirectoryContent(std::string const& dir, bool needDisk) +{ + DirectoryContent& dc = this->DirectoryContentMap[dir]; + if(needDisk && !dc.LoadedFromDisk) + { + // Load the directory content from disk. + cmsys::Directory d; + if(d.Load(dir.c_str())) + { + unsigned long n = d.GetNumberOfFiles(); + for(unsigned long i = 0; i < n; ++i) + { + const char* f = d.GetFile(i); + if(strcmp(f, ".") != 0 && strcmp(f, "..") != 0) + { + dc.insert(f); + } + } + } + dc.LoadedFromDisk = true; + } + return dc; +} diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index edb2019..dfbf1ad 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -150,6 +150,9 @@ public: ///! Get the export target set with the given name const std::vector<cmTargetExport*>* GetExportSet(const char* name) const; + /** Add a file to the manifest of generated targets for a configuration. */ + void AddToManifest(const char* config, std::string const& f); + void EnableInstallTarget(); int TryCompileTimeout; @@ -209,6 +212,13 @@ public: configuration. This is valid during generation only. */ cmTargetManifest const& GetTargetManifest() { return this->TargetManifest; } + /** Get the content of a directory on disk including the target + files to be generated. This may be called only during the + generation step. It is intended for use only by + cmComputeLinkInformation. */ + std::set<cmStdString> const& GetDirectoryContent(std::string const& dir, + bool needDisk); + void AddTarget(cmTargets::value_type &v); virtual const char* GetAllTargetName() { return "ALL_BUILD"; } @@ -304,6 +314,17 @@ private: std::vector<cmTarget const*>& steps); typedef std::map<cmTarget const*, TargetDependSet> TargetDependMap; TargetDependMap TargetDependencies; + + // Cache directory content and target files to be built. + struct DirectoryContent: public std::set<cmStdString> + { + typedef std::set<cmStdString> derived; + bool LoadedFromDisk; + DirectoryContent(): LoadedFromDisk(false) {} + DirectoryContent(DirectoryContent const& dc): + derived(dc), LoadedFromDisk(dc.LoadedFromDisk) {} + }; + std::map<cmStdString, DirectoryContent> DirectoryContentMap; }; #endif diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 89c9377..cc7e7f8 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -22,6 +22,7 @@ PURPOSE. See the above copyright notices for more information. #include "cmXCode21Object.h" #include "cmake.h" #include "cmGeneratedFileStream.h" +#include "cmComputeLinkInformation.h" #include "cmSourceFile.h" //---------------------------------------------------------------------------- @@ -2107,23 +2108,27 @@ void cmGlobalXCodeGenerator } // Compute the link library and directory information. - std::vector<cmStdString> libNames; - std::vector<cmStdString> libDirs; - std::vector<cmStdString> fullPathLibs; - this->CurrentLocalGenerator->ComputeLinkInformation(*cmtarget, configName, - libNames, libDirs, - &fullPathLibs); + cmComputeLinkInformation cli(cmtarget, configName); + if(!cli.Compute()) + { + continue; + } // Add dependencies directly on library files. - for(std::vector<cmStdString>::iterator j = fullPathLibs.begin(); - j != fullPathLibs.end(); ++j) + { + std::vector<std::string> const& libDeps = cli.GetDepends(); + for(std::vector<std::string>::const_iterator j = libDeps.begin(); + j != libDeps.end(); ++j) { target->AddDependLibrary(configName, j->c_str()); } + } - std::string linkDirs; // add the library search paths - for(std::vector<cmStdString>::const_iterator libDir = libDirs.begin(); + { + std::vector<std::string> const& libDirs = cli.GetDirectories(); + std::string linkDirs; + for(std::vector<std::string>::const_iterator libDir = libDirs.begin(); libDir != libDirs.end(); ++libDir) { if(libDir->size() && *libDir != "/usr/lib") @@ -2141,45 +2146,50 @@ void cmGlobalXCodeGenerator } this->AppendBuildSettingAttribute(target, "LIBRARY_SEARCH_PATHS", linkDirs.c_str(), configName); + } + + // add the framework search paths + { + const char* sep = ""; + std::string fdirs; + std::vector<std::string> const& fwDirs = cli.GetFrameworkPaths(); + for(std::vector<std::string>::const_iterator fdi = fwDirs.begin(); + fdi != fwDirs.end(); ++fdi) + { + fdirs += sep; + sep = " "; + fdirs += this->XCodeEscapePath(fdi->c_str()); + } + if(!fdirs.empty()) + { + this->AppendBuildSettingAttribute(target, "FRAMEWORK_SEARCH_PATHS", + fdirs.c_str(), configName); + } + } + // now add the link libraries if(cmtarget->GetType() != cmTarget::STATIC_LIBRARY) { - std::string fdirs; - std::set<cmStdString> emitted; - emitted.insert("/System/Library/Frameworks"); - for(std::vector<cmStdString>::iterator lib = libNames.begin(); - lib != libNames.end(); ++lib) + std::string linkLibs; + const char* sep = ""; + typedef cmComputeLinkInformation::ItemVector ItemVector; + ItemVector const& libNames = cli.GetItems(); + for(ItemVector::const_iterator li = libNames.begin(); + li != libNames.end(); ++li) { - std::string& libString = *lib; - // check to see if this is a -F framework path and extract it if it is - // -F framework stuff should be in the FRAMEWORK_SEARCH_PATHS and not - // OTHER_LDFLAGS - if(libString.size() > 2 && libString[0] == '-' - && libString[1] == 'F') + linkLibs += sep; + sep = " "; + if(li->IsPath) { - std::string path = libString.substr(2); - // remove escaped spaces from the path - cmSystemTools::ReplaceString(path, "\\ ", " "); - if(emitted.insert(path).second) - { - if(fdirs.size()) - { - fdirs += " "; - } - fdirs += this->XCodeEscapePath(path.c_str()); - } + linkLibs += this->XCodeEscapePath(li->Value.c_str()); } else { - this->AppendBuildSettingAttribute(target, "OTHER_LDFLAGS", - lib->c_str(), configName); + linkLibs += li->Value; } } - if(fdirs.size()) - { - this->AppendBuildSettingAttribute(target, "FRAMEWORK_SEARCH_PATHS", - fdirs.c_str(), configName); - } + this->AppendBuildSettingAttribute(target, "OTHER_LDFLAGS", + linkLibs.c_str(), configName); } } } diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index c2b14b2..60a6b72 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -16,6 +16,7 @@ =========================================================================*/ #include "cmLocalGenerator.h" +#include "cmComputeLinkInformation.h" #include "cmGeneratedFileStream.h" #include "cmGlobalGenerator.h" #include "cmInstallGenerator.h" @@ -23,7 +24,6 @@ #include "cmInstallScriptGenerator.h" #include "cmInstallTargetGenerator.h" #include "cmMakefile.h" -#include "cmOrderLinkDirectories.h" #include "cmSourceFile.h" #include "cmTest.h" #include "cmake.h" @@ -477,7 +477,7 @@ void cmLocalGenerator::GenerateInstallRules() } //---------------------------------------------------------------------------- -void cmLocalGenerator::GenerateTargetManifest(cmTargetManifest& manifest) +void cmLocalGenerator::GenerateTargetManifest() { // Collect the set of configuration types. std::vector<std::string> configNames; @@ -500,34 +500,17 @@ void cmLocalGenerator::GenerateTargetManifest(cmTargetManifest& manifest) for(cmTargets::iterator t = targets.begin(); t != targets.end(); ++t) { cmTarget& target = t->second; - cmTarget::TargetType type = target.GetType(); - if(type == cmTarget::STATIC_LIBRARY || - type == cmTarget::SHARED_LIBRARY || - type == cmTarget::MODULE_LIBRARY || - type == cmTarget::EXECUTABLE) + if(configNames.empty()) { - if(configNames.empty()) + target.GenerateTargetManifest(0); + } + else + { + for(std::vector<std::string>::iterator ci = configNames.begin(); + ci != configNames.end(); ++ci) { - manifest[""].insert(target.GetFullPath(0, false)); - if(type == cmTarget::SHARED_LIBRARY && - this->Makefile->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) - { - manifest[""].insert(target.GetFullPath(0, true)); - } - } - else - { - for(std::vector<std::string>::iterator ci = configNames.begin(); - ci != configNames.end(); ++ci) - { - const char* config = ci->c_str(); - manifest[config].insert(target.GetFullPath(config, false)); - if(type == cmTarget::SHARED_LIBRARY && - this->Makefile->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) - { - manifest[config].insert(target.GetFullPath(config, true)); - } - } + const char* config = ci->c_str(); + target.GenerateTargetManifest(config); } } } @@ -1478,50 +1461,53 @@ bool cmLocalGenerator::GetLinkerArgs(std::string& rpath, // collect all the flags needed for linking libraries linkLibs = ""; - // Try to emit each search path once - std::set<cmStdString> emitted; + const char* config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE"); + + cmComputeLinkInformation cli(&tgt, config); + if(!cli.Compute()) + { + return false; + } + + const char* linkLanguage = cli.GetLinkLanguage(); + // Embed runtime search paths if possible and if required. - bool outputRuntime = true; + bool outputRuntime = !this->Makefile->IsOn("CMAKE_SKIP_RPATH"); + + // Lookup rpath specification flags. std::string runtimeFlag; std::string runtimeSep; - - const char* config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE"); - const char* linkLanguage = - tgt.GetLinkerLanguage(this->GetGlobalGenerator()); - if(!linkLanguage) + if(tgt.GetType() != cmTarget::STATIC_LIBRARY) { - cmSystemTools:: - Error("CMake can not determine linker language for target:", - tgt.GetName()); - return false; + std::string runTimeFlagVar = "CMAKE_"; + if(tgt.GetType() == cmTarget::EXECUTABLE) + { + runTimeFlagVar += "EXECUTABLE"; + } + else + { + runTimeFlagVar += "SHARED_LIBRARY"; + } + runTimeFlagVar += "_RUNTIME_"; + runTimeFlagVar += linkLanguage; + runTimeFlagVar += "_FLAG"; + std::string runTimeFlagSepVar = runTimeFlagVar + "_SEP"; + runtimeFlag = this->Makefile->GetSafeDefinition(runTimeFlagVar.c_str()); + runtimeSep = this->Makefile->GetSafeDefinition(runTimeFlagSepVar.c_str()); } - std::string runTimeFlagVar = "CMAKE_SHARED_LIBRARY_RUNTIME_"; - runTimeFlagVar += linkLanguage; - runTimeFlagVar += "_FLAG"; - std::string runTimeFlagSepVar = runTimeFlagVar + "_SEP"; - runtimeFlag = this->Makefile->GetSafeDefinition(runTimeFlagVar.c_str()); - runtimeSep = this->Makefile->GetSafeDefinition(runTimeFlagSepVar.c_str()); - // concatenate all paths or no? - bool runtimeConcatenate = ( runtimeSep!="" ); - if(runtimeFlag == "" || this->Makefile->IsOn("CMAKE_SKIP_RPATH")) + bool runtimeConcatenate = !runtimeSep.empty(); + + const char* runtimeAlways = + this->Makefile->GetDefinition("CMAKE_PLATFORM_REQUIRED_RUNTIME_PATH"); + + // Turn off rpath support if no flag is available to specify it. + if(runtimeFlag.empty()) { outputRuntime = false; + runtimeAlways = 0; } - // Some search paths should never be emitted - emitted.insert(""); - if(const char* implicitLinks = - (this->Makefile->GetDefinition - ("CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES"))) - { - std::vector<std::string> implicitLinkVec; - cmSystemTools::ExpandListArgument(implicitLinks, implicitLinkVec); - for(unsigned int k = 0; k < implicitLinkVec.size(); ++k) - { - emitted.insert(implicitLinkVec[k]); - } - } std::string libPathFlag = this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_FLAG"); std::string libPathTerminator = @@ -1539,10 +1525,43 @@ bool cmLocalGenerator::GetLinkerArgs(std::string& rpath, linkLibs += " "; } - // Compute the link library and directory information. - std::vector<cmStdString> libNames; - std::vector<cmStdString> libDirs; - this->ComputeLinkInformation(tgt, config, libNames, libDirs); + // Append the framework search path flags. + std::vector<std::string> const& fwDirs = cli.GetFrameworkPaths(); + for(std::vector<std::string>::const_iterator fdi = fwDirs.begin(); + fdi != fwDirs.end(); ++fdi) + { + linkLibs += "-F"; + linkLibs += this->Convert(fdi->c_str(), NONE, SHELL, false); + linkLibs += " "; + } + + // Append the library search path flags. + std::vector<std::string> const& libDirs = cli.GetDirectories(); + for(std::vector<std::string>::const_iterator libDir = libDirs.begin(); + libDir != libDirs.end(); ++libDir) + { + std::string libpath = this->ConvertToOutputForExisting(libDir->c_str()); + linkLibs += libPathFlag; + linkLibs += libpath; + linkLibs += libPathTerminator; + linkLibs += " "; + } + + // Append the link items. + typedef cmComputeLinkInformation::ItemVector ItemVector; + ItemVector const& items = cli.GetItems(); + for(ItemVector::const_iterator li = items.begin(); li != items.end(); ++li) + { + if(li->IsPath) + { + linkLibs += this->Convert(li->Value.c_str(), START_OUTPUT, SHELL); + } + else + { + linkLibs += li->Value; + } + linkLibs += " "; + } // Select whether to generate an rpath for the install tree or the // build tree. @@ -1562,68 +1581,46 @@ bool cmLocalGenerator::GetLinkerArgs(std::string& rpath, { const char* install_rpath = tgt.GetProperty("INSTALL_RPATH"); cmSystemTools::ExpandListArgument(install_rpath, runtimeDirs); - for(unsigned int i=0; i < runtimeDirs.size(); ++i) - { - runtimeDirs[i] = - this->Convert(runtimeDirs[i].c_str(), FULL, SHELL, false); - } } - - // Append the library search path flags. - for(std::vector<cmStdString>::const_iterator libDir = libDirs.begin(); - libDir != libDirs.end(); ++libDir) + if(use_build_rpath || use_link_rpath) { - std::string libpath = this->ConvertToOutputForExisting(libDir->c_str()); - if(emitted.insert(libpath).second) + std::vector<std::string> const& rdirs = cli.GetRuntimeSearchPath(); + for(std::vector<std::string>::const_iterator ri = rdirs.begin(); + ri != rdirs.end(); ++ri) { - std::string fullLibPath; - if(!this->WindowsShell && this->UseRelativePaths) + // Put this directory in the rpath if using build-tree rpath + // support or if using the link path as an rpath. + if(use_build_rpath) { - fullLibPath = "\"`cd "; + runtimeDirs.push_back(*ri); } - fullLibPath += libpath; - if(!this->WindowsShell && this->UseRelativePaths) + else if(use_link_rpath) { - fullLibPath += ";pwd`\""; - } - std::string::size_type pos = libDir->find(libPathFlag.c_str()); - if((pos == std::string::npos || pos > 0) - && libDir->find("${") == std::string::npos) - { - linkLibs += libPathFlag; - linkLibs += fullLibPath; - linkLibs += libPathTerminator; - linkLibs += " "; - - // Put this directory in the rpath if using build-tree rpath - // support or if using the link path as an rpath. - if(use_build_rpath) - { - runtimeDirs.push_back(fullLibPath); - } - else if(use_link_rpath) + // Do not add any path inside the source or build tree. + const char* topSourceDir = this->Makefile->GetHomeDirectory(); + const char* topBinaryDir = this->Makefile->GetHomeOutputDirectory(); + if(!cmSystemTools::ComparePath(ri->c_str(), topSourceDir) && + !cmSystemTools::ComparePath(ri->c_str(), topBinaryDir) && + !cmSystemTools::IsSubDirectory(ri->c_str(), topSourceDir) && + !cmSystemTools::IsSubDirectory(ri->c_str(), topBinaryDir)) { - // Do not add any path inside the source or build tree. - const char* topSourceDir = this->Makefile->GetHomeDirectory(); - const char* topBinaryDir = this->Makefile->GetHomeOutputDirectory(); - if(!cmSystemTools::ComparePath(libDir->c_str(), topSourceDir) && - !cmSystemTools::ComparePath(libDir->c_str(), topBinaryDir) && - !cmSystemTools::IsSubDirectory(libDir->c_str(), topSourceDir) && - !cmSystemTools::IsSubDirectory(libDir->c_str(), topBinaryDir)) - { - runtimeDirs.push_back(fullLibPath); - } + runtimeDirs.push_back(*ri); } } } } + if(runtimeAlways) + { + // Add runtime paths required by the platform to always be + // present. This is done even when skipping rpath support. + cmSystemTools::ExpandListArgument(runtimeAlways, runtimeDirs); + } - // Append the link libraries. - for(std::vector<cmStdString>::iterator lib = libNames.begin(); - lib != libNames.end(); ++lib) + // Convert the runtime directory names for use in the build file. + for(std::vector<std::string>::iterator ri = runtimeDirs.begin(); + ri != runtimeDirs.end(); ++ri) { - linkLibs += *lib; - linkLibs += " "; + *ri = this->Convert(ri->c_str(), FULL, SHELL, false); } if(!runtimeDirs.empty()) @@ -1715,257 +1712,6 @@ void cmLocalGenerator::OutputLinkLibraries(std::ostream& fout, } //---------------------------------------------------------------------------- -void cmLocalGenerator -::ComputeLinkInformation(cmTarget& target, - const char* config, - std::vector<cmStdString>& outLibs, - std::vector<cmStdString>& outDirs, - std::vector<cmStdString>* fullPathLibs) -{ - // Compute which library configuration to link. - cmTarget::LinkLibraryType linkType = cmTarget::OPTIMIZED; - if(config && cmSystemTools::UpperCase(config) == "DEBUG") - { - linkType = cmTarget::DEBUG; - } - - // Get the language used for linking. - const char* linkLanguage = - target.GetLinkerLanguage(this->GetGlobalGenerator()); - - // Check whether we should use an import library for linking a target. - bool implib = - this->Makefile->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")?true:false; - - // On platforms without import libraries there may be a special flag - // to use when creating a plugin (module) that obtains symbols from - // the program that will load it. - const char* loader_flag = 0; - if(!implib && target.GetType() == cmTarget::MODULE_LIBRARY) - { - if(!linkLanguage) - { - cmSystemTools:: - Error("CMake can not determine linker language for target:", - target.GetName()); - return; - } - std::string loader_flag_var = "CMAKE_SHARED_MODULE_LOADER_"; - loader_flag_var += linkLanguage; - loader_flag_var += "_FLAG"; - loader_flag = this->Makefile->GetDefinition(loader_flag_var.c_str()); - } - - // Get the list of libraries against which this target wants to link. - std::vector<std::string> linkLibraries; - const cmTarget::LinkLibraryVectorType& inLibs = target.GetLinkLibraries(); - for(cmTarget::LinkLibraryVectorType::const_iterator j = inLibs.begin(); - j != inLibs.end(); ++j) - { - // For backwards compatibility variables may have been expanded - // inside library names. Clean up the resulting name. - std::string lib = j->first; - std::string::size_type pos = lib.find_first_not_of(" \t\r\n"); - if(pos != lib.npos) - { - lib = lib.substr(pos, lib.npos); - } - pos = lib.find_last_not_of(" \t\r\n"); - if(pos != lib.npos) - { - lib = lib.substr(0, pos+1); - } - if(lib.empty()) - { - continue; - } - // Link to a library if it is not the same target and is meant for - // this configuration type. - if((target.GetType() == cmTarget::EXECUTABLE || - lib != target.GetName()) && - (j->second == cmTarget::GENERAL || j->second == linkType)) - { - // Compute the proper name to use to link this library. - cmTarget* tgt = this->GlobalGenerator->FindTarget(0, lib.c_str(), false); - bool impexe = (tgt && - tgt->GetType() == cmTarget::EXECUTABLE && - tgt->GetPropertyAsBool("ENABLE_EXPORTS")); - if(impexe && !implib && !loader_flag) - { - // Skip linking to executables on platforms with no import - // libraries or loader flags. - continue; - } - else if(tgt && (tgt->GetType() == cmTarget::STATIC_LIBRARY || - tgt->GetType() == cmTarget::SHARED_LIBRARY || - tgt->GetType() == cmTarget::MODULE_LIBRARY || - impexe)) - { - // This is a CMake target. Ask the target for its real name. - std::string linkItem; - if(impexe && loader_flag) - { - // This link item is an executable that may provide symbols - // used by this target. A special flag is needed on this - // platform. Add it now. - std::string exe = tgt->GetFullPath(config, implib); - linkItem += loader_flag; - linkItem += this->Convert(exe.c_str(), NONE, SHELL, false); - } - else - { - // Pass the full path to the target file but purposely leave - // off the per-configuration subdirectory. The link directory - // ordering knows how to deal with this. - linkItem += tgt->GetDirectory(0, implib); - // on apple if the FRAMEWORK prop is set, then - // do not add the target full name but just use the directory - // name -#ifdef __APPLE__ - if (!(tgt->GetType() == cmTarget::SHARED_LIBRARY && - tgt->GetPropertyAsBool("FRAMEWORK"))) -#endif - { - linkItem += "/"; - linkItem += tgt->GetFullName(config, implib); - } - } - linkLibraries.push_back(linkItem); - // For full path, use the true location. - if(fullPathLibs) - { - fullPathLibs->push_back(tgt->GetFullPath(config, implib)); - } - } - else - { - // This is not a CMake target. Use the name given. - linkLibraries.push_back(lib); - - // Add to the list of full paths if this library is one. - if(fullPathLibs && - cmSystemTools::FileIsFullPath(lib.c_str()) && - !cmSystemTools::FileIsDirectory(lib.c_str())) - { - fullPathLibs->push_back(lib); - } - } - } - } - - // Get the list of directories the target wants to search for libraries. - const std::vector<std::string>& - linkDirectories = target.GetLinkDirectories(); - - // Lookup link type selection flags. - const char* static_link_type_flag = 0; - const char* shared_link_type_flag = 0; - const char* target_type_str = 0; - switch(target.GetType()) - { - case cmTarget::EXECUTABLE: target_type_str = "EXE"; break; - case cmTarget::SHARED_LIBRARY: target_type_str = "SHARED_LIBRARY"; break; - case cmTarget::MODULE_LIBRARY: target_type_str = "SHARED_MODULE"; break; - default: break; - } - if(target_type_str) - { - if(!linkLanguage) - { - cmSystemTools:: - Error("CMake can not determine linker language for target:", - target.GetName()); - return; - } - std::string static_link_type_flag_var = "CMAKE_"; - static_link_type_flag_var += target_type_str; - static_link_type_flag_var += "_LINK_STATIC_"; - static_link_type_flag_var += linkLanguage; - static_link_type_flag_var += "_FLAGS"; - static_link_type_flag = - this->Makefile->GetDefinition(static_link_type_flag_var.c_str()); - - std::string shared_link_type_flag_var = "CMAKE_"; - shared_link_type_flag_var += target_type_str; - shared_link_type_flag_var += "_LINK_DYNAMIC_"; - shared_link_type_flag_var += linkLanguage; - shared_link_type_flag_var += "_FLAGS"; - shared_link_type_flag = - this->Makefile->GetDefinition(shared_link_type_flag_var.c_str()); - } - - // Compute the link directory order needed to link the libraries. - cmOrderLinkDirectories orderLibs; - orderLibs.SetLinkTypeInformation(cmOrderLinkDirectories::LinkShared, - static_link_type_flag, - shared_link_type_flag); - orderLibs.AddLinkPrefix( - this->Makefile->GetDefinition("CMAKE_STATIC_LIBRARY_PREFIX")); - orderLibs.AddLinkPrefix( - this->Makefile->GetDefinition("CMAKE_SHARED_LIBRARY_PREFIX")); - - // Import library names should be matched and treated as shared - // libraries for the purposes of linking. - orderLibs.AddLinkExtension( - this->Makefile->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX"), - cmOrderLinkDirectories::LinkShared); - orderLibs.AddLinkExtension( - this->Makefile->GetDefinition("CMAKE_STATIC_LIBRARY_SUFFIX"), - cmOrderLinkDirectories::LinkStatic); - orderLibs.AddLinkExtension( - this->Makefile->GetDefinition("CMAKE_SHARED_LIBRARY_SUFFIX"), - cmOrderLinkDirectories::LinkShared); - orderLibs.AddLinkExtension( - this->Makefile->GetDefinition("CMAKE_LINK_LIBRARY_SUFFIX")); - if(const char* linkSuffixes = - this->Makefile->GetDefinition("CMAKE_EXTRA_LINK_EXTENSIONS")) - { - std::vector<std::string> linkSuffixVec; - cmSystemTools::ExpandListArgument(linkSuffixes, linkSuffixVec); - for(std::vector<std::string>::iterator i = linkSuffixVec.begin(); - i != linkSuffixVec.end(); ++i) - { - orderLibs.AddLinkExtension(i->c_str()); - } - } - std::string configSubdir; - cmGlobalGenerator* gg = this->GetGlobalGenerator(); - gg->AppendDirectoryForConfig("", config, "", configSubdir); - orderLibs.SetLinkInformation(target.GetName(), - linkLibraries, - linkDirectories, - gg->GetTargetManifest(), - configSubdir.c_str()); - orderLibs.DetermineLibraryPathOrder(); - std::vector<cmStdString> orderedLibs; - orderLibs.GetLinkerInformation(outDirs, orderedLibs); - - // Make sure libraries are linked with the proper syntax. - std::string libLinkFlag = - this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FLAG"); - std::string libLinkSuffix = - this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX"); - for(std::vector<cmStdString>::iterator l = orderedLibs.begin(); - l != orderedLibs.end(); ++l) - { - std::string lib = *l; - if(lib[0] == '-' || lib[0] == '$' || lib[0] == '`') - { - // The library is linked with special syntax by the user. - outLibs.push_back(lib); - } - else - { - // Generate the proper link syntax. - lib = libLinkFlag; - lib += *l; - lib += libLinkSuffix; - outLibs.push_back(lib); - } - } -} - -//---------------------------------------------------------------------------- void cmLocalGenerator::AddLanguageFlags(std::string& flags, const char* lang, const char* config) diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index 25bedfc..9a9be8c 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -75,7 +75,7 @@ public: /** * Generate a manifest of target files that will be built. */ - virtual void GenerateTargetManifest(cmTargetManifest&); + virtual void GenerateTargetManifest(); ///! Get the makefile for this generator cmMakefile *GetMakefile() { @@ -174,13 +174,6 @@ public: bool /*color*/) { return true; } - /** Compute the list of link libraries and directories for the given - target and configuration. */ - void ComputeLinkInformation(cmTarget& target, const char* config, - std::vector<cmStdString>& outLibs, - std::vector<cmStdString>& outDirs, - std::vector<cmStdString>* fullPathLibs=0); - /** Get the include flags for the current makefile and language. */ void GetIncludeDirectories(std::vector<std::string>& dirs, bool filter_system_dirs = true); diff --git a/Source/cmLocalVisualStudio6Generator.cxx b/Source/cmLocalVisualStudio6Generator.cxx index 2cfcf70..b462cd3 100644 --- a/Source/cmLocalVisualStudio6Generator.cxx +++ b/Source/cmLocalVisualStudio6Generator.cxx @@ -22,6 +22,8 @@ #include "cmCacheManager.h" #include "cmake.h" +#include "cmComputeLinkInformation.h" + #include <cmsys/RegularExpression.hxx> cmLocalVisualStudio6Generator::cmLocalVisualStudio6Generator() @@ -1569,12 +1571,17 @@ void cmLocalVisualStudio6Generator std::string& options) { // Compute the link information for this configuration. - std::vector<cmStdString> linkLibs; - std::vector<cmStdString> linkDirs; - this->ComputeLinkInformation(target, configName, linkLibs, linkDirs); + cmComputeLinkInformation cli(&target, configName); + if(!cli.Compute()) + { + return; + } + typedef cmComputeLinkInformation::ItemVector ItemVector; + ItemVector const& linkLibs = cli.GetItems(); + std::vector<std::string> const& linkDirs = cli.GetDirectories(); // Build the link options code. - for(std::vector<cmStdString>::const_iterator d = linkDirs.begin(); + for(std::vector<std::string>::const_iterator d = linkDirs.begin(); d != linkDirs.end(); ++d) { std::string dir = *d; @@ -1592,11 +1599,19 @@ void cmLocalVisualStudio6Generator options += "\n"; } } - for(std::vector<cmStdString>::const_iterator l = linkLibs.begin(); + for(ItemVector::const_iterator l = linkLibs.begin(); l != linkLibs.end(); ++l) { options += "# ADD LINK32 "; - options += this->ConvertToOptionallyRelativeOutputPath(l->c_str()); + if(l->IsPath) + { + options += + this->ConvertToOptionallyRelativeOutputPath(l->Value.c_str()); + } + else + { + options += l->Value; + } options += "\n"; } diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index f9ebb85..b368f7c 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -24,12 +24,24 @@ #include "cmCacheManager.h" #include "cmake.h" +#include "cmComputeLinkInformation.h" #include "cmGeneratedFileStream.h" #include <cmsys/System.h> #include <ctype.h> // for isspace +class cmLocalVisualStudio7GeneratorInternals +{ +public: + cmLocalVisualStudio7GeneratorInternals(cmLocalVisualStudio7Generator* e): + LocalGenerator(e) {} + typedef cmComputeLinkInformation::ItemVector ItemVector; + void OutputLibraries(std::ostream& fout, ItemVector const& libs); +private: + cmLocalVisualStudio7Generator* LocalGenerator; +}; + extern cmVS7FlagTable cmLocalVisualStudio7GeneratorFlagTable[]; //---------------------------------------------------------------------------- @@ -38,10 +50,12 @@ cmLocalVisualStudio7Generator::cmLocalVisualStudio7Generator() this->Version = 7; this->PlatformName = "Win32"; this->ExtraFlagTable = 0; + this->Internal = new cmLocalVisualStudio7GeneratorInternals(this); } cmLocalVisualStudio7Generator::~cmLocalVisualStudio7Generator() { + delete this->Internal; } void cmLocalVisualStudio7Generator::AddHelperCommands() @@ -748,20 +762,12 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(std::ostream& fout, targetNameImport, targetNamePDB, configName); // Compute the link library and directory information. - std::vector<cmStdString> linkLibs; - std::vector<cmStdString> linkDirs; - this->ComputeLinkInformation(target, configName, linkLibs, linkDirs); - - // Get the language to use for linking. - const char* linkLanguage = - target.GetLinkerLanguage(this->GetGlobalGenerator()); - if(!linkLanguage) + cmComputeLinkInformation cli(&target, configName); + if(!cli.Compute()) { - cmSystemTools::Error - ("CMake can not determine linker language for target:", - target.GetName()); return; } + const char* linkLanguage = cli.GetLinkLanguage(); // Compute the variable name to lookup standard libraries for this // language. @@ -777,7 +783,7 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(std::ostream& fout, fout << "\t\t\t\tAdditionalDependencies=\"$(NOINHERIT) " << this->Makefile->GetSafeDefinition(standardLibsVar.c_str()) << " "; - this->OutputLibraries(fout, linkLibs); + this->Internal->OutputLibraries(fout, cli.GetItems()); fout << "\"\n"; temp = target.GetDirectory(configName); temp += "/"; @@ -787,7 +793,7 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(std::ostream& fout, this->WriteTargetVersionAttribute(fout, target); linkOptions.OutputFlagMap(fout, "\t\t\t\t"); fout << "\t\t\t\tAdditionalLibraryDirectories=\""; - this->OutputLibraryDirectories(fout, linkDirs); + this->OutputLibraryDirectories(fout, cli.GetDirectories()); fout << "\"\n"; this->OutputModuleDefinitionFile(fout, target); temp = target.GetDirectory(configName); @@ -825,20 +831,12 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(std::ostream& fout, targetNameImport, targetNamePDB, configName); // Compute the link library and directory information. - std::vector<cmStdString> linkLibs; - std::vector<cmStdString> linkDirs; - this->ComputeLinkInformation(target, configName, linkLibs, linkDirs); - - // Get the language to use for linking. - const char* linkLanguage = - target.GetLinkerLanguage(this->GetGlobalGenerator()); - if(!linkLanguage) + cmComputeLinkInformation cli(&target, configName); + if(!cli.Compute()) { - cmSystemTools::Error - ("CMake can not determine linker language for target:", - target.GetName()); return; } + const char* linkLanguage = cli.GetLinkLanguage(); // Compute the variable name to lookup standard libraries for this // language. @@ -854,7 +852,7 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(std::ostream& fout, fout << "\t\t\t\tAdditionalDependencies=\"$(NOINHERIT) " << this->Makefile->GetSafeDefinition(standardLibsVar.c_str()) << " "; - this->OutputLibraries(fout, linkLibs); + this->Internal->OutputLibraries(fout, cli.GetItems()); fout << "\"\n"; temp = target.GetDirectory(configName); temp += "/"; @@ -864,7 +862,7 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(std::ostream& fout, this->WriteTargetVersionAttribute(fout, target); linkOptions.OutputFlagMap(fout, "\t\t\t\t"); fout << "\t\t\t\tAdditionalLibraryDirectories=\""; - this->OutputLibraryDirectories(fout, linkDirs); + this->OutputLibraryDirectories(fout, cli.GetDirectories()); fout << "\"\n"; fout << "\t\t\t\tProgramDataBaseFile=\"" << target.GetDirectory(configName) << "/" << targetNamePDB @@ -936,14 +934,23 @@ void cmLocalVisualStudio7Generator //---------------------------------------------------------------------------- void -cmLocalVisualStudio7Generator -::OutputLibraries(std::ostream& fout, - std::vector<cmStdString> const& libs) +cmLocalVisualStudio7GeneratorInternals +::OutputLibraries(std::ostream& fout, ItemVector const& libs) { - for(std::vector<cmStdString>::const_iterator l = libs.begin(); - l != libs.end(); ++l) + cmLocalVisualStudio7Generator* lg = this->LocalGenerator; + for(ItemVector::const_iterator l = libs.begin(); l != libs.end(); ++l) { - fout << this->ConvertToXMLOutputPath(l->c_str()) << " "; + if(l->IsPath) + { + std::string rel = lg->Convert(l->Value.c_str(), + cmLocalGenerator::START_OUTPUT, + cmLocalGenerator::UNCHANGED); + fout << lg->ConvertToXMLOutputPath(rel.c_str()) << " "; + } + else + { + fout << l->Value << " "; + } } } @@ -951,10 +958,10 @@ cmLocalVisualStudio7Generator void cmLocalVisualStudio7Generator ::OutputLibraryDirectories(std::ostream& fout, - std::vector<cmStdString> const& dirs) + std::vector<std::string> const& dirs) { const char* comma = ""; - for(std::vector<cmStdString>::const_iterator d = dirs.begin(); + for(std::vector<std::string>::const_iterator d = dirs.begin(); d != dirs.end(); ++d) { // Remove any trailing slash and skip empty paths. diff --git a/Source/cmLocalVisualStudio7Generator.h b/Source/cmLocalVisualStudio7Generator.h index 3687289..34af7f5 100644 --- a/Source/cmLocalVisualStudio7Generator.h +++ b/Source/cmLocalVisualStudio7Generator.h @@ -27,6 +27,7 @@ struct cmVS7FlagTable; class cmLocalVisualStudio7GeneratorOptions; class cmLocalVisualStudio7GeneratorFCInfo; +class cmLocalVisualStudio7GeneratorInternals; /** \class cmLocalVisualStudio7Generator * \brief Write Visual Studio .NET project files. @@ -96,10 +97,8 @@ private: cmTarget &target, const char *libName); void OutputBuildTool(std::ostream& fout, const char* configName, cmTarget& t); - void OutputLibraries(std::ostream& fout, - std::vector<cmStdString> const& libs); void OutputLibraryDirectories(std::ostream& fout, - std::vector<cmStdString> const& dirs); + std::vector<std::string> const& dirs); void OutputModuleDefinitionFile(std::ostream& fout, cmTarget &target); void WriteProjectStart(std::ostream& fout, const char *libName, cmTarget &tgt, std::vector<cmSourceGroup> &sgs); @@ -120,11 +119,13 @@ private: virtual std::string GetTargetDirectory(cmTarget const&) const; friend class cmLocalVisualStudio7GeneratorFCInfo; + friend class cmLocalVisualStudio7GeneratorInternals; cmVS7FlagTable const* ExtraFlagTable; std::string ModuleDefinitionFile; int Version; std::string PlatformName; // Win32 or x64 + cmLocalVisualStudio7GeneratorInternals* Internal; }; // This is a table mapping XML tag IDE names to command line options diff --git a/Source/cmOrderLinkDirectories.cxx b/Source/cmOrderLinkDirectories.cxx deleted file mode 100644 index 57b7470..0000000 --- a/Source/cmOrderLinkDirectories.cxx +++ /dev/null @@ -1,710 +0,0 @@ -#include "cmOrderLinkDirectories.h" -#include "cmSystemTools.h" -#include "cmsys/RegularExpression.hxx" -#include <ctype.h> - -//#define CM_ORDER_LINK_DIRECTORIES_DEBUG - -//------------------------------------------------------------------- -cmOrderLinkDirectories::cmOrderLinkDirectories() -{ - this->StartLinkType = LinkUnknown; - this->LinkTypeEnabled = false; - this->Debug = false; -} - -//------------------------------------------------------------------- -void -cmOrderLinkDirectories -::SetLinkTypeInformation(LinkType start_link_type, - const char* static_link_type_flag, - const char* shared_link_type_flag) -{ - // We can support link type switching only if all needed flags are - // known. - this->StartLinkType = start_link_type; - if(static_link_type_flag && *static_link_type_flag && - shared_link_type_flag && *shared_link_type_flag) - { - this->LinkTypeEnabled = true; - this->StaticLinkTypeFlag = static_link_type_flag; - this->SharedLinkTypeFlag = shared_link_type_flag; - } - else - { - this->LinkTypeEnabled = false; - this->StaticLinkTypeFlag = ""; - this->SharedLinkTypeFlag = ""; - } -} - -//------------------------------------------------------------------- -void cmOrderLinkDirectories::SetCurrentLinkType(LinkType lt) -{ - if(this->CurrentLinkType != lt) - { - this->CurrentLinkType = lt; - - if(this->LinkTypeEnabled) - { - switch(this->CurrentLinkType) - { - case LinkStatic: - this->LinkItems.push_back(this->StaticLinkTypeFlag); break; - case LinkShared: - this->LinkItems.push_back(this->SharedLinkTypeFlag); break; - default: break; - } - } - } -} - -//------------------------------------------------------------------- -bool cmOrderLinkDirectories::LibraryInDirectory(const char* desiredLib, - const char* dir, - const char* libIn) -{ - // first look for the library as given - if(this->LibraryMayConflict(desiredLib, dir, libIn)) - { - return true; - } - // next remove the extension (.a, .so ) and look for the library - // under a different name as the linker can do either - if(this->RemoveLibraryExtension.find(libIn)) - { - cmStdString lib = this->RemoveLibraryExtension.match(1); - cmStdString ext = this->RemoveLibraryExtension.match(2); - for(std::vector<cmStdString>::iterator i = this->LinkExtensions.begin(); - i != this->LinkExtensions.end(); ++i) - { - if(ext != *i) - { - std::string fname = lib; - lib += *i; - if(this->LibraryMayConflict(desiredLib, dir, fname.c_str())) - { - return true; - } - } - } - } - return false; -} - -//------------------------------------------------------------------- -void cmOrderLinkDirectories::FindLibrariesInSearchPaths() -{ - for(std::set<cmStdString>::iterator dir = this->LinkPathSet.begin(); - dir != this->LinkPathSet.end(); ++dir) - { - for(std::map<cmStdString, Library>::iterator lib - = this->FullPathLibraries.begin(); - lib != this->FullPathLibraries.end(); ++lib) - { - if(lib->second.Path != *dir) - { - if(this->LibraryInDirectory(lib->second.FullPath.c_str(), - dir->c_str(), lib->second.File.c_str())) - { - this->LibraryToDirectories[lib->second.FullPath].push_back(*dir); - } - } - } - } -} - -//------------------------------------------------------------------- -void cmOrderLinkDirectories::FindIndividualLibraryOrders() -{ - for(std::vector<Library>::iterator lib = - this->MultiDirectoryLibraries.begin(); - lib != this->MultiDirectoryLibraries.end(); ++lib) - { - std::vector<cmStdString>& dirs = - this->LibraryToDirectories[lib->FullPath]; - std::vector<std::pair<cmStdString, std::vector<cmStdString> > - >::iterator i; - for(i = this->DirectoryToAfterList.begin(); - i != this->DirectoryToAfterList.end(); ++i) - { - if(i->first == lib->Path) - { - break; - } - } - if(i == this->DirectoryToAfterList.end()) - { - std::cerr << "ERROR: should not happen\n"; - } - else - { - for(std::vector<cmStdString>::iterator d = dirs.begin(); - d != dirs.end(); ++d) - { - i->second.push_back(*d); - } - } - } -} - -//------------------------------------------------------------------- -std::string cmOrderLinkDirectories::NoCaseExpression(const char* str) -{ - std::string ret; - const char* s = str; - while(*s) - { - if(*s == '.') - { - ret += *s; - } - else - { - ret += "["; - ret += tolower(*s); - ret += toupper(*s); - ret += "]"; - } - s++; - } - return ret; -} - -//------------------------------------------------------------------- -void cmOrderLinkDirectories::CreateRegularExpressions() -{ - this->SplitFramework.compile("(.*)/(.*)\\.framework$"); - - // Compute a regex to match link extensions. - cmStdString libext = this->CreateExtensionRegex(this->LinkExtensions); - - // Create regex to remove any library extension. - cmStdString reg("(.*)"); - reg += libext; - this->RemoveLibraryExtension.compile(reg.c_str()); - - // Create a regex to match a library name. Match index 1 will be - // the prefix if it exists and empty otherwise. Match index 2 will - // be the library name. Match index 3 will be the library - // extension. - reg = "^("; - for(std::set<cmStdString>::iterator p = this->LinkPrefixes.begin(); - p != this->LinkPrefixes.end(); ++p) - { - reg += *p; - reg += "|"; - } - reg += ")"; - reg += "([^/]*)"; - - // Create a regex to match any library name. - cmStdString reg_any = reg; - reg_any += libext; -#ifdef CM_ORDER_LINK_DIRECTORIES_DEBUG - fprintf(stderr, "any regex [%s]\n", reg_any.c_str()); -#endif - this->ExtractAnyLibraryName.compile(reg_any.c_str()); - - // Create a regex to match static library names. - if(!this->StaticLinkExtensions.empty()) - { - cmStdString reg_static = reg; - reg_static += this->CreateExtensionRegex(this->StaticLinkExtensions); -#ifdef CM_ORDER_LINK_DIRECTORIES_DEBUG - fprintf(stderr, "static regex [%s]\n", reg_static.c_str()); -#endif - this->ExtractStaticLibraryName.compile(reg_static.c_str()); - } - - // Create a regex to match shared library names. - if(!this->SharedLinkExtensions.empty()) - { - cmStdString reg_shared = reg; - reg_shared += this->CreateExtensionRegex(this->SharedLinkExtensions); -#ifdef CM_ORDER_LINK_DIRECTORIES_DEBUG - fprintf(stderr, "shared regex [%s]\n", reg_shared.c_str()); -#endif - this->ExtractSharedLibraryName.compile(reg_shared.c_str()); - } -} - -//------------------------------------------------------------------- -std::string -cmOrderLinkDirectories::CreateExtensionRegex( - std::vector<cmStdString> const& exts) -{ - // Build a list of extension choices. - cmStdString libext = "("; - const char* sep = ""; - for(std::vector<cmStdString>::const_iterator i = exts.begin(); - i != exts.end(); ++i) - { - // Separate this choice from the previous one. - libext += sep; - sep = "|"; - - // Store this extension choice with the "." escaped. - libext += "\\"; -#if defined(_WIN32) && !defined(__CYGWIN__) - libext += this->NoCaseExpression(i->c_str()); -#else - libext += *i; -#endif - } - - // Finish the list. - libext += ").*"; - return libext; -} - -//------------------------------------------------------------------- -void cmOrderLinkDirectories::PrepareLinkTargets() -{ - std::vector<cmStdString> originalLinkItems = this->LinkItems; - this->LinkItems.clear(); - this->CurrentLinkType = this->StartLinkType; - for(std::vector<cmStdString>::iterator i = originalLinkItems.begin(); - i != originalLinkItems.end(); ++i) - { - // Parse out the prefix, base, and suffix components of the - // library name. If the name matches that of a shared or static - // library then set the link type accordingly. - // - // Search for shared library names first because some platforms - // have shared libraries with names that match the static library - // pattern. For example cygwin and msys use the convention - // libfoo.dll.a for import libraries and libfoo.a for static - // libraries. On AIX a library with the name libfoo.a can be - // shared! - if(this->ExtractSharedLibraryName.find(*i)) - { -#ifdef CM_ORDER_LINK_DIRECTORIES_DEBUG - fprintf(stderr, "shared regex matched [%s] [%s] [%s]\n", - this->ExtractSharedLibraryName.match(1).c_str(), - this->ExtractSharedLibraryName.match(2).c_str(), - this->ExtractSharedLibraryName.match(3).c_str()); -#endif - this->SetCurrentLinkType(LinkShared); - this->LinkItems.push_back(this->ExtractSharedLibraryName.match(2)); - } - else if(this->ExtractStaticLibraryName.find(*i)) - { -#ifdef CM_ORDER_LINK_DIRECTORIES_DEBUG - fprintf(stderr, "static regex matched [%s] [%s] [%s]\n", - this->ExtractStaticLibraryName.match(1).c_str(), - this->ExtractStaticLibraryName.match(2).c_str(), - this->ExtractStaticLibraryName.match(3).c_str()); -#endif - this->SetCurrentLinkType(LinkStatic); - this->LinkItems.push_back(this->ExtractStaticLibraryName.match(2)); - } - else if(this->ExtractAnyLibraryName.find(*i)) - { -#ifdef CM_ORDER_LINK_DIRECTORIES_DEBUG - fprintf(stderr, "any regex matched [%s] [%s] [%s]\n", - this->ExtractAnyLibraryName.match(1).c_str(), - this->ExtractAnyLibraryName.match(2).c_str(), - this->ExtractAnyLibraryName.match(3).c_str()); -#endif - this->SetCurrentLinkType(this->StartLinkType); - this->LinkItems.push_back(this->ExtractAnyLibraryName.match(2)); - } - else - { - this->SetCurrentLinkType(this->StartLinkType); - this->LinkItems.push_back(*i); - } - } - - // Restore the original linking type so system runtime libraries are - // linked properly. - this->SetCurrentLinkType(this->StartLinkType); -} - -//------------------------------------------------------------------- -bool cmOrderLinkDirectories::FindPathNotInDirectoryToAfterList( - cmStdString& path) -{ - for(std::vector<std::pair<cmStdString, std::vector<cmStdString> > - >::iterator i = this->DirectoryToAfterList.begin(); - i != this->DirectoryToAfterList.end(); ++i) - { - const cmStdString& p = i->first; - bool found = false; - for(std::vector<std::pair<cmStdString, std::vector<cmStdString> > - >::iterator j = this->DirectoryToAfterList.begin(); - j != this->DirectoryToAfterList.end() && !found; ++j) - { - if(j != i) - { - found = (std::find(j->second.begin(), j->second.end(), p) - != j->second.end()); - } - } - if(!found) - { - path = p; - this->DirectoryToAfterList.erase(i); - return true; - } - } - path = ""; - return false; -} - - -//------------------------------------------------------------------- -void cmOrderLinkDirectories::OrderPaths(std::vector<cmStdString>& - orderedPaths) -{ - cmStdString path; - // This is a topological sort implementation - // One at a time find paths that are not in any other paths after list - // and put them into the orderedPaths vector in that order - // FindPathNotInDirectoryToAfterList removes the path from the - // this->DirectoryToAfterList once it is found - while(this->FindPathNotInDirectoryToAfterList(path)) - { - orderedPaths.push_back(path); - } - // at this point if there are still paths in this->DirectoryToAfterList - // then there is a cycle and we are stuck - if(this->DirectoryToAfterList.size()) - { - for(std::vector<std::pair<cmStdString, std::vector<cmStdString> > - >::iterator i = this->DirectoryToAfterList.begin(); - i != this->DirectoryToAfterList.end(); ++i) - { - this->ImpossibleDirectories.insert(i->first); - // still put it in the path list in the order we find them - orderedPaths.push_back(i->first); - } - - } -} - -//------------------------------------------------------------------- -void cmOrderLinkDirectories::SetLinkInformation( - const char* targetName, - const std::vector<std::string>& linkLibraries, - const std::vector<std::string>& linkDirectories, - const cmTargetManifest& manifest, - const char* configSubdir - ) -{ - // Save the target name. - this->TargetName = targetName; - - // Save the subdirectory used for linking in this configuration. - this->ConfigSubdir = configSubdir? configSubdir : ""; - - // Merge the link directory search path given into our path set. - std::vector<cmStdString> empty; - for(std::vector<std::string>::const_iterator p = linkDirectories.begin(); - p != linkDirectories.end(); ++p) - { - std::string dir = *p; -#ifdef _WIN32 - // Avoid case problems for windows paths. - if(dir.size() > 2 && dir[1] == ':') - { - if(dir[0] >= 'A' && dir[0] <= 'Z') - { - dir[0] += 'a' - 'A'; - } - } - dir = cmSystemTools::GetActualCaseForPath(dir.c_str()); -#endif - if(this->DirectoryToAfterListEmitted.insert(dir).second) - { - std::pair<cmStdString, std::vector<cmStdString> > dp; - dp.first = dir; - this->DirectoryToAfterList.push_back(dp); - this->LinkPathSet.insert(dir); - } - } - - // Append the link library list into our raw list. - for(std::vector<std::string>::const_iterator l = linkLibraries.begin(); - l != linkLibraries.end(); ++l) - { - this->RawLinkItems.push_back(*l); - } - - // Construct a set of files that will exist after building. - for(cmTargetManifest::const_iterator i = manifest.begin(); - i != manifest.end(); ++i) - { - for(cmTargetSet::const_iterator j = i->second.begin(); - j != i->second.end(); ++j) - { - this->ManifestFiles.insert(*j); - } - } -} - -//------------------------------------------------------------------- -bool cmOrderLinkDirectories::DetermineLibraryPathOrder() -{ - // set up all the regular expressions - this->CreateRegularExpressions(); - std::vector<cmStdString> finalOrderPaths; - // find all libs that are full paths - Library aLib; - cmStdString dir; - cmStdString file; - std::vector<cmStdString> empty; - // do not add a -F for the system frameworks - this->EmittedFrameworkPaths.insert("/System/Library/Frameworks"); - for(unsigned int i=0; i < this->RawLinkItems.size(); ++i) - { - bool framework = false; -#ifdef CM_ORDER_LINK_DIRECTORIES_DEBUG - fprintf(stderr, "Raw link item [%s]\n", this->RawLinkItems[i].c_str()); -#endif - // if it is a full path to an item then separate it from the path - // this only works with files and paths - cmStdString& item = this->RawLinkItems[i]; - - if(cmSystemTools::FileIsFullPath(item.c_str())) - { - if(cmSystemTools::IsPathToFramework(item.c_str())) - { - this->SplitFramework.find(item.c_str()); - cmStdString path = this->SplitFramework.match(1); - // Add the -F path if we have not yet done so - if(this->EmittedFrameworkPaths.insert(path).second) - { - std::string fpath = "-F"; - fpath += cmSystemTools::ConvertToOutputPath(path.c_str()); - this->LinkItems.push_back(fpath); - } - // now add the -framework option - std::string frame = "-framework "; - frame += this->SplitFramework.match(2); - this->LinkItems.push_back(frame); - framework = true; - } - if(cmSystemTools::FileIsDirectory(item.c_str())) - { - if(!framework) - { - // A full path to a directory was found as a link item - // warn user - std::string message = - "Warning: Ignoring path found in link libraries for target: "; - message += this->TargetName; - message += ", path is: "; - message += this->RawLinkItems[i]; - message += - ". Expected a library name or a full path to a library name."; - cmSystemTools::Message(message.c_str()); - continue; - } - } // is it a directory - if(!framework) - { - dir = cmSystemTools::GetFilenamePath(this->RawLinkItems[i]); - file = cmSystemTools::GetFilenameName(this->RawLinkItems[i]); -#ifdef _WIN32 - // Avoid case problems for windows paths. - if(dir.size() > 2 && dir[1] == ':') - { - if(dir[0] >= 'A' && dir[0] <= 'Z') - { - dir[0] += 'a' - 'A'; - } - } - dir = cmSystemTools::GetActualCaseForPath(dir.c_str()); -#endif - if(this->DirectoryToAfterListEmitted.insert(dir).second) - { - std::pair<cmStdString, std::vector<cmStdString> > dp; - dp.first = dir; - this->DirectoryToAfterList.push_back(dp); - } - this->LinkPathSet.insert(dir); - aLib.FullPath = this->RawLinkItems[i]; - aLib.File = file; - aLib.Path = dir; - this->FullPathLibraries[aLib.FullPath] = aLib; -#ifdef CM_ORDER_LINK_DIRECTORIES_DEBUG - fprintf(stderr, "Storing item [%s]\n", file.c_str()); -#endif - this->LinkItems.push_back(file); - } - } - else - { - this->LinkItems.push_back(this->RawLinkItems[i]); - } - } - this->FindLibrariesInSearchPaths(); - for(std::map<cmStdString, std::vector<cmStdString> >::iterator lib = - this->LibraryToDirectories.begin(); - lib!= this->LibraryToDirectories.end(); - ++lib) - { - if(lib->second.size() > 0) - { - this->MultiDirectoryLibraries.push_back - (this->FullPathLibraries[lib->first]); - } - else - { - this->SingleDirectoryLibraries.push_back - (this->FullPathLibraries[lib->first]); - } - } - this->FindIndividualLibraryOrders(); - this->SortedSearchPaths.clear(); - if(this->Debug) - { - this->PrintMap("this->LibraryToDirectories", this->LibraryToDirectories); - this->PrintVector("this->DirectoryToAfterList", - this->DirectoryToAfterList); - } - this->OrderPaths(this->SortedSearchPaths); - // now turn libfoo.a into foo and foo.a into foo - // This will prepare the link items for -litem - this->PrepareLinkTargets(); - if(this->ImpossibleDirectories.size()) - { - cmSystemTools::Message(this->GetWarnings().c_str()); - return false; - } - return true; -} - -std::string cmOrderLinkDirectories::GetWarnings() -{ - std::string warning = - "It is impossible to order the linker search path in such a way " - "that libraries specified as full paths will be picked by the " - "linker.\nDirectories and libraries involved are:\n"; - - for(std::set<cmStdString>::iterator i = this->ImpossibleDirectories.begin(); - i != this->ImpossibleDirectories.end(); ++i) - { - warning += "Directory: "; - warning += *i; - warning += " contains:\n"; - std::map<cmStdString, std::vector<cmStdString> >::iterator j; - for(j = this->LibraryToDirectories.begin(); - j != this->LibraryToDirectories.end(); ++j) - { - if(std::find(j->second.begin(), j->second.end(), *i) - != j->second.end()) - { - warning += "Library: "; - warning += j->first; - warning += "\n"; - } - } - warning += "\n"; - } - warning += "\n"; - return warning; -} - -//------------------------------------------------------------------- -void -cmOrderLinkDirectories::PrintMap(const char* name, - std::map<cmStdString, std::vector<cmStdString> >& m) -{ - std::cout << name << "\n"; - for(std::map<cmStdString, std::vector<cmStdString> >::iterator i = - m.begin(); i != m.end(); - ++i) - { - std::cout << i->first << ": "; - for(std::vector<cmStdString>::iterator l = i->second.begin(); - l != i->second.end(); ++l) - { - std::cout << *l << " "; - } - std::cout << "\n"; - } -} -//------------------------------------------------------------------- -void -cmOrderLinkDirectories::PrintVector(const char* name, - std::vector<std::pair<cmStdString, - std::vector<cmStdString> > >& m) -{ - std::cout << name << "\n"; - for(std::vector<std::pair<cmStdString, std::vector<cmStdString> > - >::iterator i = m.begin(); i != m.end(); ++i) - { - std::cout << i->first << ": "; - for(std::vector<cmStdString>::iterator l = i->second.begin(); - l != i->second.end(); ++l) - { - std::cout << *l << " "; - } - std::cout << "\n"; - } -} - -void cmOrderLinkDirectories::GetFullPathLibraries(std::vector<cmStdString>& - libs) -{ - for(std::map<cmStdString, Library>::iterator i = - this->FullPathLibraries.begin(); - i != this->FullPathLibraries.end(); ++i) - { - libs.push_back(i->first); - } - -} - -//---------------------------------------------------------------------------- -bool cmOrderLinkDirectories::LibraryMayConflict(const char* desiredLib, - const char* dir, - const char* fname) -{ - // We need to check whether the given file may be picked up by the - // linker. This will occur if it exists as given or may be built - // using the name given. - bool found = false; - std::string path = dir; - path += "/"; - path += fname; - if(this->ManifestFiles.find(path) != this->ManifestFiles.end()) - { - found = true; - } - else if(cmSystemTools::FileExists(path.c_str())) - { - found = true; - } - - // When linking with a multi-configuration build tool the - // per-configuration subdirectory is added to each link path. Check - // this subdirectory too. - if(!found && !this->ConfigSubdir.empty()) - { - path = dir; - path += "/"; - path += this->ConfigSubdir; - path += "/"; - path += fname; - if(this->ManifestFiles.find(path) != this->ManifestFiles.end()) - { - found = true; - } - else if(cmSystemTools::FileExists(path.c_str())) - { - found = true; - } - } - - // A library conflicts if it is found and is not a symlink back to - // the desired library. - if(found) - { - return !cmSystemTools::SameFile(desiredLib, path.c_str()); - } - return false; -} diff --git a/Source/cmOrderLinkDirectories.h b/Source/cmOrderLinkDirectories.h deleted file mode 100644 index a3bc75a..0000000 --- a/Source/cmOrderLinkDirectories.h +++ /dev/null @@ -1,192 +0,0 @@ -/*========================================================================= - - 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. - -=========================================================================*/ -#ifndef cmOrderLinkDirectories_h -#define cmOrderLinkDirectories_h - -#include <cmStandardIncludes.h> -#include <map> -#include <vector> -#include "cmTarget.h" -#include "cmsys/RegularExpression.hxx" - - -/** \class cmOrderLinkDirectories - * \brief Compute the best -L path order - * - * This class computes the best order for -L paths. - * It tries to make sure full path specified libraries are - * used. For example if you have /usr/mylib/libfoo.a on as - * a link library for a target, and you also have /usr/lib/libbar.a - * and you also have /usr/lib/libfoo.a, then you would - * want -L/usr/mylib -L/usr/lib to make sure the correct libfoo.a is - * found by the linker. The algorithm is as follows: - * - foreach library create a vector of directories it exists in. - * - foreach directory create a vector of directories that must come - * after it, put this in a map<dir, vector<dir>> mapping from a directory - * to the vector of directories that it must be before. - * - put all directories into a vector - * - sort the vector with a compare function CanBeBefore - * CanBeBefore returns true if a directory is OK to be before - * another directory. This is determined by looking at the - * map<dir vector<dir>> and seeing if d1 is in the vector for d2. - */ -class cmOrderLinkDirectories -{ -public: - cmOrderLinkDirectories(); - ///! set link information from the target - void SetLinkInformation(const char* targetName, - const std::vector<std::string>& linkLibraries, - const std::vector<std::string>& linkDirectories, - const cmTargetManifest& manifest, - const char* configSubdir); - ///! Compute the best order for -L paths from GetLinkLibraries - bool DetermineLibraryPathOrder(); - ///! Get the results from DetermineLibraryPathOrder - void GetLinkerInformation(std::vector<cmStdString>& searchPaths, - std::vector<cmStdString>& linkItems) - { - linkItems = this->LinkItems; - searchPaths = this->SortedSearchPaths; - } - // should be set from CMAKE_STATIC_LIBRARY_SUFFIX, - // CMAKE_SHARED_LIBRARY_SUFFIX - // CMAKE_LINK_LIBRARY_SUFFIX - enum LinkType { LinkUnknown, LinkStatic, LinkShared }; - void AddLinkExtension(const char* e, LinkType type = LinkUnknown) - { - if(e && *e) - { - if(type == LinkStatic) - { - this->StaticLinkExtensions.push_back(e); - } - if(type == LinkShared) - { - this->SharedLinkExtensions.push_back(e); - } - this->LinkExtensions.push_back(e); - } - } - // should be set from CMAKE_STATIC_LIBRARY_PREFIX - void AddLinkPrefix(const char* s) - { - if(s) - { - this->LinkPrefixes.insert(s); - } - } - // Return any warnings if the exist - std::string GetWarnings(); - // return a list of all full path libraries - void GetFullPathLibraries(std::vector<cmStdString>& libs); - - // Provide flags for switching library link type. - void SetLinkTypeInformation(LinkType start_link_type, - const char* static_link_type_flag, - const char* shared_link_type_flag); - - // structure to hold a full path library link item - struct Library - { - cmStdString FullPath; - cmStdString File; - cmStdString Path; - }; - friend struct cmOrderLinkDirectoriesCompare; - void DebugOn() - { - this->Debug = true; - } - -private: - void CreateRegularExpressions(); - std::string CreateExtensionRegex(std::vector<cmStdString> const& exts); - void DetermineLibraryPathOrder(std::vector<cmStdString>& searchPaths, - std::vector<cmStdString>& libs, - std::vector<cmStdString>& sortedPaths); - void PrepareLinkTargets(); - bool LibraryInDirectory(const char* desiredLib, - const char* dir, const char* lib); - void FindLibrariesInSearchPaths(); - void FindIndividualLibraryOrders(); - void PrintMap(const char* name, - std::map<cmStdString, std::vector<cmStdString> >& m); - void PrintVector(const char* name, - std::vector<std::pair<cmStdString, - std::vector<cmStdString> > >& m); - void OrderPaths(std::vector<cmStdString>& paths); - bool FindPathNotInDirectoryToAfterList(cmStdString& path); - std::string NoCaseExpression(const char* str); - bool LibraryMayConflict(const char* desiredLib, - const char* dir, const char* fname); -private: - // set of files that will exist when the build occurs - std::set<cmStdString> ManifestFiles; - // map from library to directories that it is in other than its full path - std::map<cmStdString, std::vector<cmStdString> > LibraryToDirectories; - // map from directory to vector of directories that must be after it - std::vector<std::pair<cmStdString, std::vector<cmStdString> > > - DirectoryToAfterList; - std::set<cmStdString> DirectoryToAfterListEmitted; - // map from full path to a Library struct - std::map<cmStdString, Library> FullPathLibraries; - // libraries that are found in multiple directories - std::vector<Library> MultiDirectoryLibraries; - // libraries that are only found in one directory - std::vector<Library> SingleDirectoryLibraries; - // This is a vector of all the link objects -lm or m - std::vector<cmStdString> LinkItems; - // Unprocessed link items - std::vector<cmStdString> RawLinkItems; - // This vector holds the sorted -L paths - std::vector<cmStdString> SortedSearchPaths; - // This vector holds the -F paths - std::set<cmStdString> EmittedFrameworkPaths; - // This is the set of -L paths unsorted, but unique - std::set<cmStdString> LinkPathSet; - // the names of link extensions - std::vector<cmStdString> StaticLinkExtensions; - std::vector<cmStdString> SharedLinkExtensions; - std::vector<cmStdString> LinkExtensions; - // the names of link prefixes - std::set<cmStdString> LinkPrefixes; - // set of directories that can not be put in the correct order - std::set<cmStdString> ImpossibleDirectories; - // Name of target - cmStdString TargetName; - // Subdirectory used for this configuration if any. - cmStdString ConfigSubdir; - - // Link type adjustment. - LinkType StartLinkType; - LinkType CurrentLinkType; - cmStdString StaticLinkTypeFlag; - cmStdString SharedLinkTypeFlag; - bool LinkTypeEnabled; - void SetCurrentLinkType(LinkType lt); - - // library regular expressions - cmsys::RegularExpression RemoveLibraryExtension; - cmsys::RegularExpression ExtractStaticLibraryName; - cmsys::RegularExpression ExtractSharedLibraryName; - cmsys::RegularExpression ExtractAnyLibraryName; - cmsys::RegularExpression SplitFramework; - bool Debug; -}; - -#endif diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 787e08e..ab303ab 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -35,7 +35,6 @@ cmTarget::cmTarget() { this->Makefile = 0; this->LinkLibrariesAnalyzed = false; - this->LinkDirectoriesComputed = false; this->HaveInstallRule = false; this->DLLPlatform = false; this->IsImportedTarget = false; @@ -843,71 +842,15 @@ void cmTarget::MergeLinkLibraries( cmMakefile& mf, void cmTarget::AddLinkDirectory(const char* d) { // Make sure we don't add unnecessary search directories. - if(std::find(this->ExplicitLinkDirectories.begin(), - this->ExplicitLinkDirectories.end(), d) - == this->ExplicitLinkDirectories.end() ) + if(this->LinkDirectoriesEmmitted.insert(d).second) { - this->ExplicitLinkDirectories.push_back( d ); - this->LinkDirectoriesComputed = false; + this->LinkDirectories.push_back(d); } } //---------------------------------------------------------------------------- const std::vector<std::string>& cmTarget::GetLinkDirectories() { - // Make sure all library dependencies have been analyzed. - if(!this->LinkLibrariesAnalyzed && !this->LinkLibraries.empty()) - { - cmSystemTools::Error( - "cmTarget::GetLinkDirectories called before " - "cmTarget::AnalyzeLibDependencies on target ", - this->Name.c_str()); - } - - // Make sure the complete set of link directories has been computed. - if(!this->LinkDirectoriesComputed) - { - // Check whether we should use an import library for linking a target. - bool implib = - this->Makefile->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX") != 0; - - // Compute the full set of link directories including the - // locations of targets that have been linked in. Start with the - // link directories given explicitly. - this->LinkDirectories = this->ExplicitLinkDirectories; - for(LinkLibraryVectorType::iterator ll = this->LinkLibraries.begin(); - ll != this->LinkLibraries.end(); ++ll) - { - // If this library is a CMake target then add its location as a - // link directory. - std::string lib = ll->first; - cmTarget* tgt = 0; - if(this->Makefile && this->Makefile->GetLocalGenerator() && - this->Makefile->GetLocalGenerator()->GetGlobalGenerator()) - { - tgt = (this->Makefile->GetLocalGenerator()->GetGlobalGenerator() - ->FindTarget(0, lib.c_str(), false)); - } - if(tgt) - { - // Add the directory only if it is not already present. This - // is an N^2 algorithm for adding the directories, but N - // should not get very big. - const char* libpath = tgt->GetDirectory(0, implib); - if(std::find(this->LinkDirectories.begin(), - this->LinkDirectories.end(), - libpath) == this->LinkDirectories.end()) - { - this->LinkDirectories.push_back(libpath); - } - } - } - - // The complete set of link directories has now been computed. - this->LinkDirectoriesComputed = true; - } - - // Return the complete set of link directories. return this->LinkDirectories; } @@ -2245,6 +2188,76 @@ void cmTarget::GetExecutableNamesInternal(std::string& name, } //---------------------------------------------------------------------------- +void cmTarget::GenerateTargetManifest(const char* config) +{ + cmMakefile* mf = this->Makefile; + cmLocalGenerator* lg = mf->GetLocalGenerator(); + cmGlobalGenerator* gg = lg->GetGlobalGenerator(); + + // Get the names. + std::string name; + std::string soName; + std::string realName; + std::string impName; + std::string pdbName; + if(this->GetType() == cmTarget::EXECUTABLE) + { + this->GetExecutableNames(name, realName, impName, pdbName, config); + } + else if(this->GetType() == cmTarget::STATIC_LIBRARY || + this->GetType() == cmTarget::SHARED_LIBRARY || + this->GetType() == cmTarget::MODULE_LIBRARY) + { + this->GetLibraryNames(name, soName, realName, impName, pdbName, config); + } + else + { + return; + } + + // Get the directory. + std::string dir = this->GetDirectory(config, false); + + // Add each name. + std::string f; + if(!name.empty()) + { + f = dir; + f += "/"; + f += name; + gg->AddToManifest(config? config:"", f); + } + if(!soName.empty()) + { + f = dir; + f += "/"; + f += soName; + gg->AddToManifest(config? config:"", f); + } + if(!realName.empty()) + { + f = dir; + f += "/"; + f += realName; + gg->AddToManifest(config? config:"", f); + } + if(!pdbName.empty()) + { + f = dir; + f += "/"; + f += pdbName; + gg->AddToManifest(config? config:"", f); + } + if(!impName.empty()) + { + f = this->GetDirectory(config, true); + f += "/"; + f += impName; + gg->AddToManifest(config? config:"", f); + } +} + +//---------------------------------------------------------------------------- void cmTarget::SetPropertyDefault(const char* property, const char* default_value) { diff --git a/Source/cmTarget.h b/Source/cmTarget.h index d1b48e6..50e8429 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -271,6 +271,9 @@ public: std::string& impName, std::string& pdbName, const char* config); + /** Add the target output files to the global generator manifest. */ + void GenerateTargetManifest(const char* config); + /** * Compute whether this target must be relinked before installing. */ @@ -414,10 +417,9 @@ private: LinkLibraryVectorType LinkLibraries; LinkLibraryVectorType PrevLinkedLibraries; bool LinkLibrariesAnalyzed; - bool LinkDirectoriesComputed; std::vector<std::string> Frameworks; std::vector<std::string> LinkDirectories; - std::vector<std::string> ExplicitLinkDirectories; + std::set<cmStdString> LinkDirectoriesEmmitted; bool HaveInstallRule; std::string InstallNameFixupPath; std::string InstallPath; |