diff options
Diffstat (limited to 'Source/cmComputeLinkInformation.cxx')
-rw-r--r-- | Source/cmComputeLinkInformation.cxx | 1869 |
1 files changed, 1869 insertions, 0 deletions
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx new file mode 100644 index 000000000..df78bf886 --- /dev/null +++ b/Source/cmComputeLinkInformation.cxx @@ -0,0 +1,1869 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmComputeLinkInformation.h" + +#include "cmComputeLinkDepends.h" +#include "cmOrderDirectories.h" + +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmTarget.h" +#include "cmake.h" + +#include <ctype.h> + +//#define CM_COMPUTE_LINK_INFO_DEBUG + +/* +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). + +------------------------------------------------------------------------------ +Notes about dependent (transitive) shared libraries: + +On non-Windows systems shared libraries may have transitive +dependencies. In order to support LINK_INTERFACE_LIBRARIES we must +support linking to a shared library without listing all the libraries +to which it links. Some linkers want to be able to find the +transitive dependencies (dependent libraries) of shared libraries +listed on the command line. + + - On Windows, DLLs are not directly linked, and the import libraries + have no transitive dependencies. + + - On Mac OS X 10.5 and above transitive dependencies are not needed. + + - On Mac OS X 10.4 and below we need to actually list the dependencies. + Otherwise when using -isysroot for universal binaries it cannot + find the dependent libraries. Listing them on the command line + tells the linker where to find them, but unfortunately also links + the library. + + - On HP-UX, the linker wants to find the transitive dependencies of + shared libraries in the -L paths even if the dependent libraries + are given on the link line. + + - On AIX the transitive dependencies are not needed. + + - On SGI, the linker wants to find the transitive dependencies of + shared libraries in the -L paths if they are not given on the link + line. Transitive linking can be disabled using the options + + -no_transitive_link -Wl,-no_transitive_link + + which disable it. Both options must be given when invoking the + linker through the compiler. + + - On Sun, the linker wants to find the transitive dependencies of + shared libraries in the -L paths if they are not given on the link + line. + + - On Linux, FreeBSD, and QNX: + + The linker wants to find the transitive dependencies of shared + libraries in the "-rpath-link" paths option if they have not been + given on the link line. The option is like rpath but just for + link time: + + -Wl,-rpath-link,"/path1:/path2" + +For -rpath-link, we need a separate runtime path ordering pass +including just the dependent libraries that are not linked. + +For -L paths on non-HP, we can do the same thing as with rpath-link +but put the results in -L paths. The paths should be listed at the +end to avoid conflicting with user search paths (?). + +For -L paths on HP, we should do a runtime path ordering pass with +all libraries, both linked and non-linked. Even dependent +libraries that are also linked need to be listed in -L paths. + +In our implementation we add all dependent libraries to the runtime +path computation. Then the auto-generated RPATH will find everything. + +------------------------------------------------------------------------------ +Notes about shared libraries with not builtin soname: + +Some UNIX shared libraries may be created with no builtin soname. On +some platforms such libraries cannot be linked using the path to their +location because the linker will copy the path into the field used to +find the library at runtime. + + Apple: ../libfoo.dylib ==> libfoo.dylib # ok, uses install_name + SGI: ../libfoo.so ==> libfoo.so # ok + AIX: ../libfoo.so ==> libfoo.so # ok + Linux: ../libfoo.so ==> ../libfoo.so # bad + HP-UX: ../libfoo.so ==> ../libfoo.so # bad + Sun: ../libfoo.so ==> ../libfoo.so # bad + FreeBSD: ../libfoo.so ==> ../libfoo.so # bad + +In order to link these libraries we need to use the old-style split +into -L.. and -lfoo options. This should be fairly safe because most +problems with -lfoo options were related to selecting shared libraries +instead of static but in this case we want the shared lib. Link +directory ordering needs to be done to make sure these shared +libraries are found first. There should be very few restrictions +because this need be done only for shared libraries without soname-s. + +*/ + +//---------------------------------------------------------------------------- +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(); + this->CMakeInstance = this->GlobalGenerator->GetCMakeInstance(); + + // Check whether to recognize OpenBSD-style library versioned names. + this->OpenBSD = this->Makefile->GetCMakeInstance() + ->GetPropertyAsBool("FIND_LIBRARY_USE_OPENBSD_VERSIONING"); + + // The configuration being linked. + this->Config = config; + + // Allocate internals. + this->OrderLinkerSearchPath = + new cmOrderDirectories(this->GlobalGenerator, target, + "linker search path"); + this->OrderRuntimeSearchPath = + new cmOrderDirectories(this->GlobalGenerator, target, + "runtime search path"); + this->OrderDependentRPath = 0; + + // Get the language used for linking this target. + this->LinkLanguage = this->Target->GetLinkerLanguage(config); + if(!this->LinkLanguage) + { + // The Compute method will do nothing, so skip the rest of the + // initialization. + return; + } + + // 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->LibLinkFileFlag = + this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FILE_FLAG"); + this->LibLinkSuffix = + this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX"); + + // Get options needed to specify RPATHs. + this->RuntimeUseChrpath = false; + if(this->Target->GetType() != cmTarget::STATIC_LIBRARY) + { + const char* tType = + ((this->Target->GetType() == cmTarget::EXECUTABLE)? + "EXECUTABLE" : "SHARED_LIBRARY"); + std::string rtVar = "CMAKE_"; + rtVar += tType; + rtVar += "_RUNTIME_"; + rtVar += this->LinkLanguage; + rtVar += "_FLAG"; + std::string rtSepVar = rtVar + "_SEP"; + this->RuntimeFlag = this->Makefile->GetSafeDefinition(rtVar.c_str()); + this->RuntimeSep = this->Makefile->GetSafeDefinition(rtSepVar.c_str()); + this->RuntimeAlways = + (this->Makefile-> + GetSafeDefinition("CMAKE_PLATFORM_REQUIRED_RUNTIME_PATH")); + this->RuntimeUseChrpath = this->Target->IsChrpathUsed(config); + + // Get options needed to help find dependent libraries. + std::string rlVar = "CMAKE_"; + rlVar += tType; + rlVar += "_RPATH_LINK_"; + rlVar += this->LinkLanguage; + rlVar += "_FLAG"; + this->RPathLinkFlag = this->Makefile->GetSafeDefinition(rlVar.c_str()); + } + + // 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"; + this->LinkWithRuntimePath = this->Makefile->IsOn(var.c_str()); + } + + // Check the platform policy for missing soname case. + this->NoSONameUsesPath = + this->Makefile->IsOn("CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME"); + + // Get link type information. + this->ComputeLinkTypeInfo(); + + // Setup the link item parser. + this->ComputeItemParserInfo(); + + // Setup framework support. + this->ComputeFrameworkInfo(); + + // Choose a mode for dealing with shared library dependencies. + this->SharedDependencyMode = SharedDepModeNone; + if(this->Makefile->IsOn("CMAKE_LINK_DEPENDENT_LIBRARY_FILES")) + { + this->SharedDependencyMode = SharedDepModeLink; + } + else if(this->Makefile->IsOn("CMAKE_LINK_DEPENDENT_LIBRARY_DIRS")) + { + this->SharedDependencyMode = SharedDepModeLibDir; + } + else if(!this->RPathLinkFlag.empty()) + { + this->SharedDependencyMode = SharedDepModeDir; + this->OrderDependentRPath = + new cmOrderDirectories(this->GlobalGenerator, target, + "dependent library path"); + } + + // Add the search path entries requested by the user to path ordering. + this->OrderLinkerSearchPath + ->AddUserDirectories(this->Target->GetLinkDirectories()); + this->OrderRuntimeSearchPath + ->AddUserDirectories(this->Target->GetLinkDirectories()); + + // Set up the implicit link directories. + this->LoadImplicitLinkInfo(); + this->OrderLinkerSearchPath + ->SetImplicitDirectories(this->ImplicitLinkDirs); + this->OrderRuntimeSearchPath + ->SetImplicitDirectories(this->ImplicitLinkDirs); + if(this->OrderDependentRPath) + { + this->OrderDependentRPath + ->SetImplicitDirectories(this->ImplicitLinkDirs); + this->OrderDependentRPath + ->AddLanguageDirectories(this->RuntimeLinkDirs); + } + + // Decide whether to enable compatible library search path mode. + // There exists code that effectively does + // + // /path/to/libA.so -lB + // + // where -lB is meant to link to /path/to/libB.so. This is broken + // because it specified -lB without specifying a link directory (-L) + // in which to search for B. This worked in CMake 2.4 and below + // because -L/path/to would be added by the -L/-l split for A. In + // order to support such projects we need to add the directories + // containing libraries linked with a full path to the -L path. + this->OldLinkDirMode = + this->Target->GetPolicyStatusCMP0003() != cmPolicies::NEW; + if(this->OldLinkDirMode) + { + // Construct a mask to not bother with this behavior for link + // directories already specified by the user. + std::vector<std::string> const& dirs = this->Target->GetLinkDirectories(); + for(std::vector<std::string>::const_iterator di = dirs.begin(); + di != dirs.end(); ++di) + { + this->OldLinkDirMask.insert(*di); + } + } +} + +//---------------------------------------------------------------------------- +cmComputeLinkInformation::~cmComputeLinkInformation() +{ + delete this->OrderLinkerSearchPath; + delete this->OrderRuntimeSearchPath; + delete this->OrderDependentRPath; +} + +//---------------------------------------------------------------------------- +cmComputeLinkInformation::ItemVector const& +cmComputeLinkInformation::GetItems() +{ + return this->Items; +} + +//---------------------------------------------------------------------------- +std::vector<std::string> const& cmComputeLinkInformation::GetDirectories() +{ + return this->OrderLinkerSearchPath->GetOrderedDirectories(); +} + +//---------------------------------------------------------------------------- +std::string cmComputeLinkInformation::GetRPathLinkString() +{ + // If there is no separate linker runtime search flag (-rpath-link) + // there is no reason to compute a string. + if(!this->OrderDependentRPath) + { + return ""; + } + + // Construct the linker runtime search path. + std::string rpath_link; + const char* sep = ""; + std::vector<std::string> const& dirs = + this->OrderDependentRPath->GetOrderedDirectories(); + for(std::vector<std::string>::const_iterator di = dirs.begin(); + di != dirs.end(); ++di) + { + rpath_link += sep; + sep = ":"; + rpath_link += *di; + } + return rpath_link; +} + +//---------------------------------------------------------------------------- +std::vector<std::string> const& cmComputeLinkInformation::GetDepends() +{ + return this->Depends; +} + +//---------------------------------------------------------------------------- +std::vector<std::string> const& cmComputeLinkInformation::GetFrameworkPaths() +{ + return this->FrameworkPaths; +} + +//---------------------------------------------------------------------------- +std::set<cmTarget*> const& +cmComputeLinkInformation::GetSharedLibrariesLinked() +{ + return this->SharedLibrariesLinked; +} + +//---------------------------------------------------------------------------- +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 the ordered link line items. + cmComputeLinkDepends cld(this->Target, this->Config); + cld.SetOldLinkDirMode(this->OldLinkDirMode); + cmComputeLinkDepends::EntryVector const& linkEntries = cld.Compute(); + + // Add the link line items. + for(cmComputeLinkDepends::EntryVector::const_iterator + lei = linkEntries.begin(); + lei != linkEntries.end(); ++lei) + { + if(lei->IsSharedDep) + { + this->AddSharedDepItem(lei->Item, lei->Target); + } + else + { + this->AddItem(lei->Item, lei->Target); + } + } + + // Restore the target link type so the correct system runtime + // libraries are found. + const char* lss = this->Target->GetProperty("LINK_SEARCH_END_STATIC"); + if(cmSystemTools::IsOn(lss)) + { + this->SetCurrentLinkType(LinkStatic); + } + else + { + this->SetCurrentLinkType(this->StartLinkType); + } + + // Finish listing compatibility paths. + if(this->OldLinkDirMode) + { + // For CMake 2.4 bug-compatibility we need to consider the output + // directories of targets linked in another configuration as link + // directories. + std::set<cmTarget*> const& wrongItems = cld.GetOldWrongConfigItems(); + for(std::set<cmTarget*>::const_iterator i = wrongItems.begin(); + i != wrongItems.end(); ++i) + { + cmTarget* tgt = *i; + bool implib = + (this->UseImportLibrary && + (tgt->GetType() == cmTarget::SHARED_LIBRARY)); + std::string lib = tgt->GetFullPath(this->Config , implib, true); + this->OldLinkDirItems.push_back(lib); + } + } + + // Finish setting up linker search directories. + if(!this->FinishLinkerSearchDirectories()) + { + return false; + } + + // Add implicit language runtime libraries and directories. + this->AddImplicitLinkInfo(); + + return true; +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::AddImplicitLinkInfo() +{ + // The link closure lists all languages whose implicit info is needed. + cmTarget::LinkClosure const* lc=this->Target->GetLinkClosure(this->Config); + for(std::vector<std::string>::const_iterator li = lc->Languages.begin(); + li != lc->Languages.end(); ++li) + { + // Skip those of the linker language. They are implicit. + if(*li != this->LinkLanguage) + { + this->AddImplicitLinkInfo(*li); + } + } +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::AddImplicitLinkInfo(std::string const& lang) +{ + // Add libraries for this language that are not implied by the + // linker language. + std::string libVar = "CMAKE_"; + libVar += lang; + libVar += "_IMPLICIT_LINK_LIBRARIES"; + if(const char* libs = this->Makefile->GetDefinition(libVar.c_str())) + { + std::vector<std::string> libsVec; + cmSystemTools::ExpandListArgument(libs, libsVec); + for(std::vector<std::string>::const_iterator i = libsVec.begin(); + i != libsVec.end(); ++i) + { + if(this->ImplicitLinkLibs.find(*i) == this->ImplicitLinkLibs.end()) + { + this->AddItem(i->c_str(), 0); + } + } + } + + // Add linker search paths for this language that are not + // implied by the linker language. + std::string dirVar = "CMAKE_"; + dirVar += lang; + dirVar += "_IMPLICIT_LINK_DIRECTORIES"; + if(const char* dirs = this->Makefile->GetDefinition(dirVar.c_str())) + { + std::vector<std::string> dirsVec; + cmSystemTools::ExpandListArgument(dirs, dirsVec); + this->OrderLinkerSearchPath->AddLanguageDirectories(dirsVec); + } +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::AddItem(std::string const& item, cmTarget* tgt) +{ + // Compute the proper name to use to link this library. + const char* config = this->Config; + bool impexe = (tgt && tgt->IsExecutableWithExports()); + if(impexe && !this->UseImportLibrary && !this->LoaderFlag) + { + // Skip linking to executables on platforms with no import + // libraries or loader flags. + return; + } + + if(tgt && tgt->IsLinkable()) + { + // 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, this->UseImportLibrary, + true); + linkItem += exe; + this->Items.push_back(Item(linkItem, true, tgt)); + this->Depends.push_back(exe); + } + else + { + // Decide whether to use an import library. + bool implib = + (this->UseImportLibrary && + (impexe || tgt->GetType() == cmTarget::SHARED_LIBRARY)); + + // Pass the full path to the target file. + std::string lib = tgt->GetFullPath(config, implib, true); + this->Depends.push_back(lib); + + this->AddTargetItem(lib, tgt); + 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->Depends.push_back(item); + this->AddFullItem(item); + this->AddLibraryRuntimeInfo(item); + } + } + else + { + // This is a library or option specified by the user. + this->AddUserItem(item, true); + } + } +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::AddSharedDepItem(std::string const& item, + cmTarget* tgt) +{ + // If dropping shared library dependencies, ignore them. + if(this->SharedDependencyMode == SharedDepModeNone) + { + return; + } + + // The user may have incorrectly named an item. Skip items that are + // not full paths to shared libraries. + if(tgt) + { + // The target will provide a full path. Make sure it is a shared + // library. + if(tgt->GetType() != cmTarget::SHARED_LIBRARY) + { + return; + } + } + else + { + // Skip items that are not full paths. We will not be able to + // reliably specify them. + if(!cmSystemTools::FileIsFullPath(item.c_str())) + { + return; + } + + // Get the name of the library from the file name. + std::string file = cmSystemTools::GetFilenameName(item); + if(!this->ExtractSharedLibraryName.find(file.c_str())) + { + // This is not the name of a shared library. + return; + } + } + + // If in linking mode, just link to the shared library. + if(this->SharedDependencyMode == SharedDepModeLink) + { + this->AddItem(item, tgt); + return; + } + + // Get a full path to the dependent shared library. + // Add it to the runtime path computation so that the target being + // linked will be able to find it. + std::string lib; + if(tgt) + { + lib = tgt->GetFullPath(this->Config, this->UseImportLibrary); + this->AddLibraryRuntimeInfo(lib, tgt); + } + else + { + lib = item; + this->AddLibraryRuntimeInfo(lib); + } + + // Check if we need to include the dependent shared library in other + // path ordering. + cmOrderDirectories* order = 0; + if(this->SharedDependencyMode == SharedDepModeLibDir && + !this->LinkWithRuntimePath /* AddLibraryRuntimeInfo adds it */) + { + // Add the item to the linker search path. + order = this->OrderLinkerSearchPath; + } + else if(this->SharedDependencyMode == SharedDepModeDir) + { + // Add the item to the separate dependent library search path. + order = this->OrderDependentRPath; + } + if(order) + { + if(tgt) + { + std::string soName = tgt->GetSOName(this->Config); + const char* soname = soName.empty()? 0 : soName.c_str(); + order->AddRuntimeLibrary(lib, soname); + } + else + { + order->AddRuntimeLibrary(lib); + } + } +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::ComputeLinkTypeInfo() +{ + // Check whether archives may actually be shared libraries. + this->ArchivesMayBeShared = + this->CMakeInstance->GetPropertyAsBool( + "TARGET_ARCHIVES_MAY_BE_SHARED_LIBS"); + + // 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; + } + + // Lookup the starting link type from the target (linked statically?). + const char* lss = this->Target->GetProperty("LINK_SEARCH_START_STATIC"); + this->StartLinkType = cmSystemTools::IsOn(lss)? LinkStatic : 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); + } + } + if(const char* sharedSuffixes = + mf->GetDefinition("CMAKE_EXTRA_SHARED_LIBRARY_SUFFIXES")) + { + std::vector<std::string> sharedSuffixVec; + cmSystemTools::ExpandListArgument(sharedSuffixes, sharedSuffixVec); + for(std::vector<std::string>::iterator i = sharedSuffixVec.begin(); + i != sharedSuffixVec.end(); ++i) + { + this->AddLinkExtension(i->c_str(), LinkShared); + } + } + + // 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->OrderLinkerSearchPath->SetLinkExtensionInfo(this->LinkExtensions, + reg); + + // 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; + this->SharedRegexString = + this->CreateExtensionRegex(this->SharedLinkExtensions); + reg_shared += this->SharedRegexString; +#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 && *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 += ")"; + + // Add an optional OpenBSD version component. + if(this->OpenBSD) + { + libext += "(\\.[0-9]+\\.[0-9]+)?"; + } + + 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 += static_cast<char>(tolower(*s)); + ret += static_cast<char>(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::AddTargetItem(std::string const& item, + cmTarget* target) +{ + // This is called to handle a link item that is a full path to a target. + // If the target is not a static library make sure the link type is + // shared. This is because dynamic-mode linking can handle both + // shared and static libraries but static-mode can handle only + // static libraries. If a previous user item changed the link type + // to static we need to make sure it is back to shared. + if(target->GetType() != cmTarget::STATIC_LIBRARY) + { + this->SetCurrentLinkType(LinkShared); + } + + // Keep track of shared library targets linked. + if(target->GetType() == cmTarget::SHARED_LIBRARY) + { + this->SharedLibrariesLinked.insert(target); + } + + // Handle case of an imported shared library with no soname. + if(this->NoSONameUsesPath && + target->IsImportedSharedLibWithoutSOName(this->Config)) + { + this->AddSharedLibNoSOName(item); + return; + } + + // If this platform wants a flag before the full path, add it. + if(!this->LibLinkFileFlag.empty()) + { + this->Items.push_back(Item(this->LibLinkFileFlag, false)); + } + + // For compatibility with CMake 2.4 include the item's directory in + // the linker search path. + if(this->OldLinkDirMode && !target->IsFrameworkOnApple() && + this->OldLinkDirMask.find(cmSystemTools::GetFilenamePath(item)) == + this->OldLinkDirMask.end()) + { + this->OldLinkDirItems.push_back(item); + } + + // Now add the full path to the library. + this->Items.push_back(Item(item, true, target)); +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::AddFullItem(std::string const& item) +{ + // Check for the implicit link directory special case. + if(this->CheckImplicitDirItem(item)) + { + return; + } + + // Check for case of shared library with no builtin soname. + if(this->NoSONameUsesPath && this->CheckSharedLibNoSOName(item)) + { + return; + } + + // Full path libraries should specify a valid library file name. + // See documentation of CMP0008. + if(this->Target->GetPolicyStatusCMP0008() != cmPolicies::NEW && + (strstr(this->GlobalGenerator->GetName(), "Visual Studio") || + strstr(this->GlobalGenerator->GetName(), "Xcode"))) + { + std::string file = cmSystemTools::GetFilenameName(item); + if(!this->ExtractAnyLibraryName.find(file.c_str())) + { + this->HandleBadFullItem(item, file); + return; + } + } + + // This is called to handle a link item that is a full path. + // If the target is not a static library make sure the link type is + // shared. This is because dynamic-mode linking can handle both + // shared and static libraries but static-mode can handle only + // static libraries. If a previous user item changed the link type + // to static we need to make sure it is back to shared. + if(this->LinkTypeEnabled) + { + std::string name = cmSystemTools::GetFilenameName(item); + if(this->ExtractSharedLibraryName.find(name)) + { + this->SetCurrentLinkType(LinkShared); + } + else if(!this->ExtractStaticLibraryName.find(item)) + { + // We cannot determine the type. Assume it is the target's + // default type. + this->SetCurrentLinkType(this->StartLinkType); + } + } + + // For compatibility with CMake 2.4 include the item's directory in + // the linker search path. + if(this->OldLinkDirMode && + this->OldLinkDirMask.find(cmSystemTools::GetFilenamePath(item)) == + this->OldLinkDirMask.end()) + { + this->OldLinkDirItems.push_back(item); + } + + // If this platform wants a flag before the full path, add it. + if(!this->LibLinkFileFlag.empty()) + { + this->Items.push_back(Item(this->LibLinkFileFlag, false)); + } + + // Now add the full path to the library. + this->Items.push_back(Item(item, true)); +} + +//---------------------------------------------------------------------------- +bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item) +{ + // We only switch to a pathless item if the link type may be + // enforced. Fortunately only platforms that support link types + // seem to have magic per-architecture implicit link directories. + if(!this->LinkTypeEnabled) + { + return false; + } + + // Check if this item is in an implicit link directory. + std::string dir = cmSystemTools::GetFilenamePath(item); + if(this->ImplicitLinkDirs.find(dir) == this->ImplicitLinkDirs.end()) + { + // Only libraries in implicit link directories are converted to + // pathless items. + return false; + } + + // Only apply the policy below if the library file is one that can + // be found by the linker. + std::string file = cmSystemTools::GetFilenameName(item); + if(!this->ExtractAnyLibraryName.find(file)) + { + return false; + } + + // Many system linkers support multiple architectures by + // automatically selecting the implicit linker search path for the + // current architecture. If the library appears in an implicit link + // directory then just report the file name without the directory + // portion. This will allow the system linker to locate the proper + // library for the architecture at link time. + this->AddUserItem(file, false); + + // Make sure the link directory ordering will find the library. + this->OrderLinkerSearchPath->AddLinkLibrary(item); + + return true; +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::AddUserItem(std::string const& item, + bool pathNotKnown) +{ + // 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 + + // Pass flags through untouched. + if(item[0] == '-' || item[0] == '$' || item[0] == '`') + { + // if this is a -l option then we might need to warn about + // CMP0003 so put it in OldUserFlagItems, if it is not a -l + // or -Wl,-l (-framework -pthread), then allow it without a + // CMP0003 as -L will not affect those other linker flags + if(item.find("-l") == 0 || item.find("-Wl,-l") == 0) + { + // This is a linker option provided by the user. + this->OldUserFlagItems.push_back(item); + } + + // 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; + } + + // 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! + std::string lib; + 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 + { + // This is a name specified by the user. + if(pathNotKnown) + { + this->OldUserFlagItems.push_back(item); + } + + // 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. + this->Items.push_back(Item("-framework", false)); + std::string fw = this->SplitFramework.match(2); + fw = this->LocalGenerator->EscapeForShell(fw.c_str()); + this->Items.push_back(Item(fw, false)); +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::AddDirectoryItem(std::string const& item) +{ + if(this->Makefile->IsOn("APPLE") + && cmSystemTools::IsPathToFramework(item.c_str())) + { + this->AddFrameworkItem(item); + } + else + { + 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); + } +} + +//---------------------------------------------------------------------------- +bool cmComputeLinkInformation::CheckSharedLibNoSOName(std::string const& item) +{ + // This platform will use the path to a library as its soname if the + // library is given via path and was not built with an soname. If + // this is a shared library that might be the case. + std::string file = cmSystemTools::GetFilenameName(item); + if(this->ExtractSharedLibraryName.find(file)) + { + // If we can guess the soname fairly reliably then assume the + // library has one. Otherwise assume the library has no builtin + // soname. + std::string soname; + if(!cmSystemTools::GuessLibrarySOName(item, soname)) + { + this->AddSharedLibNoSOName(item); + return true; + } + } + return false; +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::AddSharedLibNoSOName(std::string const& item) +{ + // We have a full path to a shared library with no soname. We need + // to ask the linker to locate the item because otherwise the path + // we give to it will be embedded in the target linked. Then at + // runtime the dynamic linker will search for the library using the + // path instead of just the name. + std::string file = cmSystemTools::GetFilenameName(item); + this->AddUserItem(file, false); + + // Make sure the link directory ordering will find the library. + this->OrderLinkerSearchPath->AddLinkLibrary(item); +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::HandleBadFullItem(std::string const& item, + std::string const& file) +{ + // Do not depend on things that do not exist. + std::vector<std::string>::iterator i = + std::find(this->Depends.begin(), this->Depends.end(), item); + if(i != this->Depends.end()) + { + this->Depends.erase(i); + } + + // Tell the linker to search for the item and provide the proper + // path for it. Do not contribute to any CMP0003 warning (do not + // put in OldLinkDirItems or OldUserFlagItems). + this->AddUserItem(file, false); + this->OrderLinkerSearchPath->AddLinkLibrary(item); + + // Produce any needed message. + switch(this->Target->GetPolicyStatusCMP0008()) + { + case cmPolicies::WARN: + { + // Print the warning at most once for this item. + std::string wid = "CMP0008-WARNING-GIVEN-"; + wid += item; + if(!this->CMakeInstance->GetPropertyAsBool(wid.c_str())) + { + this->CMakeInstance->SetProperty(wid.c_str(), "1"); + cmOStringStream w; + w << (this->Makefile->GetPolicies() + ->GetPolicyWarning(cmPolicies::CMP0008)) << "\n" + << "Target \"" << this->Target->GetName() << "\" links to item\n" + << " " << item << "\n" + << "which is a full-path but not a valid library file name."; + this->CMakeInstance->IssueMessage(cmake::AUTHOR_WARNING, w.str(), + this->Target->GetBacktrace()); + } + } + case cmPolicies::OLD: + // OLD behavior does not warn. + break; + case cmPolicies::NEW: + // NEW behavior will not get here. + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + { + cmOStringStream e; + e << (this->Makefile->GetPolicies()-> + GetRequiredPolicyError(cmPolicies::CMP0008)) << "\n" + << "Target \"" << this->Target->GetName() << "\" links to item\n" + << " " << item << "\n" + << "which is a full-path but not a valid library file name."; + this->CMakeInstance->IssueMessage(cmake::FATAL_ERROR, e.str(), + this->Target->GetBacktrace()); + } + break; + } +} + +//---------------------------------------------------------------------------- +bool cmComputeLinkInformation::FinishLinkerSearchDirectories() +{ + // Support broken projects if necessary. + if(this->OldLinkDirItems.empty() || this->OldUserFlagItems.empty() || + !this->OldLinkDirMode) + { + return true; + } + + // Enforce policy constraints. + switch(this->Target->GetPolicyStatusCMP0003()) + { + case cmPolicies::WARN: + if(!this->CMakeInstance->GetPropertyAsBool("CMP0003-WARNING-GIVEN")) + { + this->CMakeInstance->SetProperty("CMP0003-WARNING-GIVEN", "1"); + cmOStringStream w; + this->PrintLinkPolicyDiagnosis(w); + this->CMakeInstance->IssueMessage(cmake::AUTHOR_WARNING, w.str(), + this->Target->GetBacktrace()); + } + case cmPolicies::OLD: + // OLD behavior is to add the paths containing libraries with + // known full paths as link directories. + break; + case cmPolicies::NEW: + // Should never happen due to assignment of OldLinkDirMode + return true; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + { + cmOStringStream e; + e << (this->Makefile->GetPolicies()-> + GetRequiredPolicyError(cmPolicies::CMP0003)) << "\n"; + this->PrintLinkPolicyDiagnosis(e); + this->CMakeInstance->IssueMessage(cmake::FATAL_ERROR, e.str(), + this->Target->GetBacktrace()); + return false; + } + } + + // Add the link directories for full path items. + for(std::vector<std::string>::const_iterator + i = this->OldLinkDirItems.begin(); + i != this->OldLinkDirItems.end(); ++i) + { + this->OrderLinkerSearchPath->AddLinkLibrary(*i); + } + return true; +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::PrintLinkPolicyDiagnosis(std::ostream& os) +{ + // Tell the user what to do. + os << "Policy CMP0003 should be set before this line. " + << "Add code such as\n" + << " if(COMMAND cmake_policy)\n" + << " cmake_policy(SET CMP0003 NEW)\n" + << " endif(COMMAND cmake_policy)\n" + << "as early as possible but after the most recent call to " + << "cmake_minimum_required or cmake_policy(VERSION). "; + + // List the items that might need the old-style paths. + os << "This warning appears because target \"" + << this->Target->GetName() << "\" " + << "links to some libraries for which the linker must search:\n"; + { + // Format the list of unknown items to be as short as possible while + // still fitting in the allowed width (a true solution would be the + // bin packing problem if we were allowed to change the order). + std::string::size_type max_size = 76; + std::string line; + const char* sep = " "; + for(std::vector<std::string>::const_iterator + i = this->OldUserFlagItems.begin(); + i != this->OldUserFlagItems.end(); ++i) + { + // If the addition of another item will exceed the limit then + // output the current line and reset it. Note that the separator + // is either " " or ", " which is always 2 characters. + if(!line.empty() && (line.size() + i->size() + 2) > max_size) + { + os << line << "\n"; + sep = " "; + line = ""; + } + line += sep; + line += *i; + // Convert to the other separator. + sep = ", "; + } + if(!line.empty()) + { + os << line << "\n"; + } + } + + // List the paths old behavior is adding. + os << "and other libraries with known full path:\n"; + std::set<cmStdString> emitted; + for(std::vector<std::string>::const_iterator + i = this->OldLinkDirItems.begin(); + i != this->OldLinkDirItems.end(); ++i) + { + if(emitted.insert(cmSystemTools::GetFilenamePath(*i)).second) + { + os << " " << *i << "\n"; + } + } + + // Explain. + os << "CMake is adding directories in the second list to the linker " + << "search path in case they are needed to find libraries from the " + << "first list (for backwards compatibility with CMake 2.4). " + << "Set policy CMP0003 to OLD or NEW to enable or disable this " + << "behavior explicitly. " + << "Run \"cmake --help-policy CMP0003\" for more information."; +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::LoadImplicitLinkInfo() +{ + std::vector<std::string> implicitDirVec; + + // Get platform-wide implicit directories. + if(const char* implicitLinks = + (this->Makefile->GetDefinition + ("CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES"))) + { + cmSystemTools::ExpandListArgument(implicitLinks, implicitDirVec); + } + + // Append library architecture to all implicit platform directories + // and add them to the set + if(const char* libraryArch = + this->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE")) + { + for (std::vector<std::string>::const_iterator i = implicitDirVec.begin(); + i != implicitDirVec.end(); ++i) + { + this->ImplicitLinkDirs.insert(*i + "/" + libraryArch); + } + } + + // Get language-specific implicit directories. + std::string implicitDirVar = "CMAKE_"; + implicitDirVar += this->LinkLanguage; + implicitDirVar += "_IMPLICIT_LINK_DIRECTORIES"; + if(const char* implicitDirs = + this->Makefile->GetDefinition(implicitDirVar.c_str())) + { + cmSystemTools::ExpandListArgument(implicitDirs, implicitDirVec); + } + + // Store implicit link directories. + for(std::vector<std::string>::const_iterator i = implicitDirVec.begin(); + i != implicitDirVec.end(); ++i) + { + this->ImplicitLinkDirs.insert(*i); + } + + // Get language-specific implicit libraries. + std::vector<std::string> implicitLibVec; + std::string implicitLibVar = "CMAKE_"; + implicitLibVar += this->LinkLanguage; + implicitLibVar += "_IMPLICIT_LINK_LIBRARIES"; + if(const char* implicitLibs = + this->Makefile->GetDefinition(implicitLibVar.c_str())) + { + cmSystemTools::ExpandListArgument(implicitLibs, implicitLibVec); + } + + // Store implicit link libraries. + for(std::vector<std::string>::const_iterator i = implicitLibVec.begin(); + i != implicitLibVec.end(); ++i) + { + // Items starting in '-' but not '-l' are flags, not libraries, + // and should not be filtered by this implicit list. + std::string const& item = *i; + if(item[0] != '-' || item[1] == 'l') + { + this->ImplicitLinkLibs.insert(item); + } + } + + // Get platform specific rpath link directories + if(const char *rpathDirs = + (this->Makefile->GetDefinition + ("CMAKE_PLATFORM_RUNTIME_PATH"))) + { + cmSystemTools::ExpandListArgument(rpathDirs, this->RuntimeLinkDirs); + } +} + +//---------------------------------------------------------------------------- +std::vector<std::string> const& +cmComputeLinkInformation::GetRuntimeSearchPath() +{ + return this->OrderRuntimeSearchPath->GetOrderedDirectories(); +} + +//---------------------------------------------------------------------------- +void +cmComputeLinkInformation::AddLibraryRuntimeInfo(std::string const& fullPath, + cmTarget* target) +{ + // Libraries with unknown type must be handled using just the file + // on disk. + if(target->GetType() == cmTarget::UNKNOWN_LIBRARY) + { + this->AddLibraryRuntimeInfo(fullPath); + return; + } + + // 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 = target->GetSOName(this->Config); + const char* soname = soName.empty()? 0 : soName.c_str(); + + // Include this library in the runtime path ordering. + this->OrderRuntimeSearchPath->AddRuntimeLibrary(fullPath, soname); + if(this->LinkWithRuntimePath) + { + this->OrderLinkerSearchPath->AddRuntimeLibrary(fullPath, soname); + } +} + +//---------------------------------------------------------------------------- +void +cmComputeLinkInformation::AddLibraryRuntimeInfo(std::string const& fullPath) +{ + // Get the name of the library from the file name. + std::string file = cmSystemTools::GetFilenameName(fullPath); + if(!this->ExtractSharedLibraryName.find(file.c_str())) + { + // On some platforms (AIX) a shared library may look static. + if(this->ArchivesMayBeShared) + { + if(!this->ExtractStaticLibraryName.find(file.c_str())) + { + // This is not the name of a shared library or archive. + return; + } + } + else + { + // This is not the name of a shared library. + return; + } + } + + // Include this library in the runtime path ordering. + this->OrderRuntimeSearchPath->AddRuntimeLibrary(fullPath); + if(this->LinkWithRuntimePath) + { + this->OrderLinkerSearchPath->AddRuntimeLibrary(fullPath); + } +} + +//---------------------------------------------------------------------------- +void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs, + bool for_install) +{ + // Select whether to generate runtime search directories. + bool outputRuntime = + !this->Makefile->IsOn("CMAKE_SKIP_RPATH") && !this->RuntimeFlag.empty(); + + // Select whether to generate an rpath for the install tree or the + // build tree. + bool linking_for_install = + (for_install || + this->Target->GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH")); + bool use_install_rpath = + (outputRuntime && this->Target->HaveInstallTreeRPATH() && + linking_for_install); + bool use_build_rpath = + (outputRuntime && this->Target->HaveBuildTreeRPATH() && + !linking_for_install); + bool use_link_rpath = + outputRuntime && linking_for_install && + !this->Makefile->IsOn("CMAKE_SKIP_INSTALL_RPATH") && + this->Target->GetPropertyAsBool("INSTALL_RPATH_USE_LINK_PATH"); + + // Construct the RPATH. + if(use_install_rpath) + { + const char* install_rpath = this->Target->GetProperty("INSTALL_RPATH"); + cmSystemTools::ExpandListArgument(install_rpath, runtimeDirs); + } + if(use_build_rpath || use_link_rpath) + { + std::vector<std::string> const& rdirs = this->GetRuntimeSearchPath(); + for(std::vector<std::string>::const_iterator ri = rdirs.begin(); + ri != rdirs.end(); ++ri) + { + // 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(*ri); + } + 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)) + { + runtimeDirs.push_back(*ri); + } + } + } + } + + // Add runtime paths required by the platform to always be + // present. This is done even when skipping rpath support. + cmSystemTools::ExpandListArgument(this->RuntimeAlways.c_str(), runtimeDirs); +} + +//---------------------------------------------------------------------------- +std::string cmComputeLinkInformation::GetRPathString(bool for_install) +{ + // Get the directories to use. + std::vector<std::string> runtimeDirs; + this->GetRPath(runtimeDirs, for_install); + + // Concatenate the paths. + std::string rpath; + const char* sep = ""; + for(std::vector<std::string>::const_iterator ri = runtimeDirs.begin(); + ri != runtimeDirs.end(); ++ri) + { + // Separate from previous path. + rpath += sep; + sep = this->GetRuntimeSep().c_str(); + + // Add this path. + rpath += *ri; + } + + // If the rpath will be replaced at install time, prepare space. + if(!for_install && this->RuntimeUseChrpath) + { + if(!rpath.empty()) + { + // Add one trailing separator so the linker does not re-use the + // rpath .dynstr entry for a symbol name that happens to match + // the end of the rpath string. + rpath += this->GetRuntimeSep(); + } + + // Make sure it is long enough to hold the replacement value. + std::string::size_type minLength = this->GetChrpathString().length(); + while(rpath.length() < minLength) + { + rpath += this->GetRuntimeSep(); + } + } + + return rpath; +} + +//---------------------------------------------------------------------------- +std::string cmComputeLinkInformation::GetChrpathString() +{ + if(!this->RuntimeUseChrpath) + { + return ""; + } + + return this->GetRPathString(true); +} |