|  | /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying | 
|  | file LICENSE.rst or https://cmake.org/licensing for details.  */ | 
|  | #include "cmComputeLinkInformation.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <cctype> | 
|  | #include <sstream> | 
|  | #include <utility> | 
|  |  | 
|  | #include <cm/memory> | 
|  | #include <cm/optional> | 
|  | #include <cm/string_view> | 
|  | #include <cmext/algorithm> | 
|  | #include <cmext/string_view> | 
|  |  | 
|  | #include "cmComputeLinkDepends.h" | 
|  | #include "cmGeneratorTarget.h" | 
|  | #include "cmGlobalGenerator.h" | 
|  | #include "cmLinkItem.h" | 
|  | #include "cmList.h" | 
|  | #include "cmListFileCache.h" | 
|  | #include "cmLocalGenerator.h" | 
|  | #include "cmMakefile.h" | 
|  | #include "cmMessageType.h" | 
|  | #include "cmOrderDirectories.h" | 
|  | #include "cmPlaceholderExpander.h" | 
|  | #include "cmSourceFile.h" | 
|  | #include "cmState.h" | 
|  | #include "cmStateTypes.h" | 
|  | #include "cmStringAlgorithms.h" | 
|  | #include "cmSystemTools.h" | 
|  | #include "cmTarget.h" | 
|  | #include "cmValue.h" | 
|  | #include "cmXcFramework.h" | 
|  | #include "cmake.h" | 
|  |  | 
|  | // #define CM_COMPUTE_LINK_INFO_DEBUG | 
|  |  | 
|  | /* | 
|  | Notes about linking on various platforms: | 
|  |  | 
|  | ------------------------------------------------------------------------------ | 
|  |  | 
|  | Linux, FreeBSD, macOS, 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( | 
|  | cmGeneratorTarget const* target, std::string const& config) | 
|  | // Store context information. | 
|  | : Target(target) | 
|  | , Makefile(target->Target->GetMakefile()) | 
|  | , GlobalGenerator(target->GetLocalGenerator()->GetGlobalGenerator()) | 
|  | , CMakeInstance(this->GlobalGenerator->GetCMakeInstance()) | 
|  | // The configuration being linked. | 
|  | , Config(config) | 
|  | { | 
|  | // Check whether to recognize OpenBSD-style library versioned names. | 
|  | this->IsOpenBSD = this->Makefile->GetState()->GetGlobalPropertyAsBool( | 
|  | "FIND_LIBRARY_USE_OPENBSD_VERSIONING"); | 
|  |  | 
|  | // Allocate internals. | 
|  | this->OrderLinkerSearchPath = cm::make_unique<cmOrderDirectories>( | 
|  | this->GlobalGenerator, target, "linker search path"); | 
|  | this->OrderRuntimeSearchPath = cm::make_unique<cmOrderDirectories>( | 
|  | this->GlobalGenerator, target, "runtime search path"); | 
|  |  | 
|  | // Get the language used for linking this target. | 
|  | this->LinkLanguage = this->Target->GetLinkerLanguage(config); | 
|  | if (this->LinkLanguage.empty()) { | 
|  | // The Compute method will do nothing, so skip the rest of the | 
|  | // initialization. | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Check whether we should skip dependencies on shared library files. | 
|  | this->LinkDependsNoShared = | 
|  | this->Target->GetPropertyAsBool("LINK_DEPENDS_NO_SHARED"); | 
|  |  | 
|  | // 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. | 
|  | if (!this->Target->IsDLLPlatform() && | 
|  | this->Target->GetType() == cmStateEnums::MODULE_LIBRARY) { | 
|  | std::string loader_flag_var = | 
|  | cmStrCat("CMAKE_SHARED_MODULE_LOADER_", this->LinkLanguage, "_FLAG"); | 
|  | this->LoaderFlag = this->Makefile->GetDefinition(loader_flag_var); | 
|  | } | 
|  |  | 
|  | // Get options needed to link libraries. | 
|  | if (cmValue flag = this->Makefile->GetDefinition( | 
|  | cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_LIBRARY_FLAG"))) { | 
|  | this->LibLinkFlag = *flag; | 
|  | } else { | 
|  | this->LibLinkFlag = | 
|  | this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FLAG"); | 
|  | } | 
|  | if (cmValue flag = this->Makefile->GetDefinition( | 
|  | cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_LIBRARY_FILE_FLAG"))) { | 
|  | this->LibLinkFileFlag = *flag; | 
|  | } else { | 
|  | this->LibLinkFileFlag = | 
|  | this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FILE_FLAG"); | 
|  | } | 
|  | if (cmValue suffix = this->Makefile->GetDefinition( | 
|  | cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_LIBRARY_SUFFIX"))) { | 
|  | this->LibLinkSuffix = *suffix; | 
|  | } else { | 
|  | this->LibLinkSuffix = | 
|  | this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX"); | 
|  | } | 
|  | if (cmValue flag = this->Makefile->GetDefinition( | 
|  | cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_OBJECT_FILE_FLAG"))) { | 
|  | this->ObjLinkFileFlag = *flag; | 
|  | } else { | 
|  | this->ObjLinkFileFlag = | 
|  | this->Makefile->GetSafeDefinition("CMAKE_LINK_OBJECT_FILE_FLAG"); | 
|  | } | 
|  |  | 
|  | // Get options needed to specify RPATHs. | 
|  | this->RuntimeUseChrpath = false; | 
|  | if (this->Target->GetType() != cmStateEnums::STATIC_LIBRARY) { | 
|  | char const* tType = ((this->Target->GetType() == cmStateEnums::EXECUTABLE) | 
|  | ? "EXECUTABLE" | 
|  | : "SHARED_LIBRARY"); | 
|  | std::string rtVar = | 
|  | cmStrCat("CMAKE_", tType, "_RUNTIME_", this->LinkLanguage, "_FLAG"); | 
|  | std::string rtSepVar = cmStrCat(rtVar, "_SEP"); | 
|  | this->RuntimeFlag = this->Makefile->GetSafeDefinition(rtVar); | 
|  | this->RuntimeSep = this->Makefile->GetSafeDefinition(rtSepVar); | 
|  | 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 = | 
|  | cmStrCat("CMAKE_", tType, "_RPATH_LINK_", this->LinkLanguage, "_FLAG"); | 
|  | this->RPathLinkFlag = this->Makefile->GetSafeDefinition(rlVar); | 
|  | } | 
|  |  | 
|  | // Check if we need to include the runtime search path at link time. | 
|  | { | 
|  | std::string var = cmStrCat("CMAKE_SHARED_LIBRARY_LINK_", | 
|  | this->LinkLanguage, "_WITH_RUNTIME_PATH"); | 
|  | this->LinkWithRuntimePath = this->Makefile->IsOn(var); | 
|  | } | 
|  |  | 
|  | // Define some Feature descriptors to handle standard library and object link | 
|  | if (!this->GetLibLinkFileFlag().empty()) { | 
|  | this->LibraryFeatureDescriptors.emplace( | 
|  | "__CMAKE_LINK_LIBRARY", | 
|  | LibraryFeatureDescriptor{ | 
|  | "__CMAKE_LINK_LIBRARY", | 
|  | cmStrCat(this->GetLibLinkFileFlag(), "<LIBRARY>") }); | 
|  | } | 
|  | if (!this->GetObjLinkFileFlag().empty()) { | 
|  | this->LibraryFeatureDescriptors.emplace( | 
|  | "__CMAKE_LINK_OBJECT", | 
|  | LibraryFeatureDescriptor{ | 
|  | "__CMAKE_LINK_OBJECT", | 
|  | cmStrCat(this->GetObjLinkFileFlag(), "<LIBRARY>") }); | 
|  | } | 
|  | if (!this->LoaderFlag->empty()) { | 
|  | // Define a Feature descriptor for the link of an executable with exports | 
|  | this->LibraryFeatureDescriptors.emplace( | 
|  | "__CMAKE_LINK_EXECUTABLE", | 
|  | LibraryFeatureDescriptor{ "__CMAKE_LINK_EXECUTABLE", | 
|  | cmStrCat(*this->LoaderFlag, "<LIBRARY>") }); | 
|  | } | 
|  | // To link framework using a full path | 
|  | this->LibraryFeatureDescriptors.emplace( | 
|  | "__CMAKE_LINK_FRAMEWORK", | 
|  | LibraryFeatureDescriptor{ "__CMAKE_LINK_FRAMEWORK", "<LIBRARY>" }); | 
|  | // To link xcframework using a full path | 
|  | this->LibraryFeatureDescriptors.emplace( | 
|  | "__CMAKE_LINK_XCFRAMEWORK", | 
|  | LibraryFeatureDescriptor{ "__CMAKE_LINK_XCFRAMEWORK", "<LIBRARY>" }); | 
|  |  | 
|  | // 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 = cm::make_unique<cmOrderDirectories>( | 
|  | this->GlobalGenerator, target, "dependent library path"); | 
|  | } | 
|  |  | 
|  | // Add the search path entries requested by the user to path ordering. | 
|  | std::vector<std::string> directories; | 
|  | this->Target->GetLinkDirectories(directories, config, this->LinkLanguage); | 
|  | this->OrderLinkerSearchPath->AddUserDirectories(directories); | 
|  | this->OrderRuntimeSearchPath->AddUserDirectories(directories); | 
|  |  | 
|  | // 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); | 
|  | } | 
|  | } | 
|  |  | 
|  | cmComputeLinkInformation::~cmComputeLinkInformation() = default; | 
|  |  | 
|  | namespace { | 
|  | std::string const& DEFAULT = cmComputeLinkDepends::LinkEntry::DEFAULT; | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::AppendValues( | 
|  | std::string& result, std::vector<BT<std::string>>& values) | 
|  | { | 
|  | for (BT<std::string>& p : values) { | 
|  | if (result.empty()) { | 
|  | result.append(" "); | 
|  | } | 
|  |  | 
|  | result.append(p.Value); | 
|  | } | 
|  | } | 
|  |  | 
|  | cmComputeLinkInformation::ItemVector const& | 
|  | cmComputeLinkInformation::GetItems() const | 
|  | { | 
|  | return this->Items; | 
|  | } | 
|  |  | 
|  | std::vector<std::string> const& cmComputeLinkInformation::GetDirectories() | 
|  | const | 
|  | { | 
|  | return this->OrderLinkerSearchPath->GetOrderedDirectories(); | 
|  | } | 
|  |  | 
|  | std::vector<BT<std::string>> | 
|  | cmComputeLinkInformation::GetDirectoriesWithBacktraces() | 
|  | { | 
|  | std::vector<BT<std::string>> directoriesWithBacktraces; | 
|  |  | 
|  | std::vector<BT<std::string>> targetLinkDirectories = | 
|  | this->Target->GetLinkDirectories(this->Config, this->LinkLanguage); | 
|  |  | 
|  | std::vector<std::string> const& orderedDirectories = this->GetDirectories(); | 
|  | for (std::string const& dir : orderedDirectories) { | 
|  | auto result = std::find(targetLinkDirectories.begin(), | 
|  | targetLinkDirectories.end(), dir); | 
|  | if (result != targetLinkDirectories.end()) { | 
|  | directoriesWithBacktraces.emplace_back(std::move(*result)); | 
|  | } else { | 
|  | directoriesWithBacktraces.emplace_back(dir); | 
|  | } | 
|  | } | 
|  |  | 
|  | return directoriesWithBacktraces; | 
|  | } | 
|  |  | 
|  | std::string cmComputeLinkInformation::GetRPathLinkString() const | 
|  | { | 
|  | // 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. These MUST NOT contain tokens | 
|  | // such as $ORIGIN, see https://sourceware.org/bugzilla/show_bug.cgi?id=16936 | 
|  | return cmJoin(this->OrderDependentRPath->GetOrderedDirectories(), ":"); | 
|  | } | 
|  |  | 
|  | std::vector<std::string> const& cmComputeLinkInformation::GetDepends() const | 
|  | { | 
|  | return this->Depends; | 
|  | } | 
|  |  | 
|  | std::vector<std::string> const& cmComputeLinkInformation::GetFrameworkPaths() | 
|  | const | 
|  | { | 
|  | return this->FrameworkPaths; | 
|  | } | 
|  |  | 
|  | std::set<std::string> const& | 
|  | cmComputeLinkInformation::GetFrameworkPathsEmitted() const | 
|  | { | 
|  | return this->FrameworkPathsEmitted; | 
|  | } | 
|  |  | 
|  | std::vector<std::string> const& | 
|  | cmComputeLinkInformation::GetXcFrameworkHeaderPaths() const | 
|  | { | 
|  | return this->XcFrameworkHeaderPaths; | 
|  | } | 
|  |  | 
|  | std::set<cmGeneratorTarget const*> const& | 
|  | cmComputeLinkInformation::GetSharedLibrariesLinked() const | 
|  | { | 
|  | return this->SharedLibrariesLinked; | 
|  | } | 
|  |  | 
|  | std::vector<cmGeneratorTarget const*> const& | 
|  | cmComputeLinkInformation::GetExternalObjectTargets() const | 
|  | { | 
|  | return this->ExternalObjectTargets; | 
|  | } | 
|  |  | 
|  | bool cmComputeLinkInformation::Compute() | 
|  | { | 
|  | // Skip targets that do not link or have link-like information consumers may | 
|  | // need (namely modules). | 
|  | if (!(this->Target->GetType() == cmStateEnums::EXECUTABLE || | 
|  | this->Target->GetType() == cmStateEnums::SHARED_LIBRARY || | 
|  | this->Target->GetType() == cmStateEnums::MODULE_LIBRARY || | 
|  | this->Target->GetType() == cmStateEnums::STATIC_LIBRARY || | 
|  | (this->Target->CanCompileSources() && | 
|  | (this->Target->HaveCxxModuleSupport(this->Config) == | 
|  | cmGeneratorTarget::Cxx20SupportLevel::Supported || | 
|  | this->Target->HaveFortranSources())))) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // We require a link language for the target. | 
|  | if (this->LinkLanguage.empty()) { | 
|  | cmSystemTools::Error( | 
|  | "CMake can not determine linker language for target: " + | 
|  | this->Target->GetName()); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | LinkLibrariesStrategy strategy = LinkLibrariesStrategy::REORDER_MINIMALLY; | 
|  | if (cmValue s = this->Target->GetProperty("LINK_LIBRARIES_STRATEGY")) { | 
|  | if (*s == "REORDER_MINIMALLY"_s) { | 
|  | strategy = LinkLibrariesStrategy::REORDER_MINIMALLY; | 
|  | } else if (*s == "REORDER_FREELY"_s) { | 
|  | strategy = LinkLibrariesStrategy::REORDER_FREELY; | 
|  | } else { | 
|  | this->CMakeInstance->IssueMessage( | 
|  | MessageType::FATAL_ERROR, | 
|  | cmStrCat("LINK_LIBRARIES_STRATEGY value '", *s, | 
|  | "' is not recognized."), | 
|  | this->Target->GetBacktrace()); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Compute the ordered link line items. | 
|  | cmComputeLinkDepends cld(this->Target, this->Config, this->LinkLanguage, | 
|  | strategy); | 
|  | cmComputeLinkDepends::EntryVector const& linkEntries = cld.Compute(); | 
|  | FeatureDescriptor const* currentFeature = nullptr; | 
|  |  | 
|  | // Add the link line items. | 
|  | for (cmComputeLinkDepends::LinkEntry const& linkEntry : linkEntries) { | 
|  | if (linkEntry.Kind == cmComputeLinkDepends::LinkEntry::Group) { | 
|  | auto const& groupFeature = this->GetGroupFeature(linkEntry.Feature); | 
|  | if (groupFeature.Supported) { | 
|  | if (linkEntry.Item.Value == "</LINK_GROUP>" && currentFeature) { | 
|  | // emit feature suffix, if any | 
|  | if (!currentFeature->Suffix.empty()) { | 
|  | this->Items.emplace_back( | 
|  | BT<std::string>{ currentFeature->Suffix, | 
|  | this->Items.back().Value.Backtrace }, | 
|  | ItemIsPath::No); | 
|  | } | 
|  | currentFeature = nullptr; | 
|  | } | 
|  | this->Items.emplace_back( | 
|  | BT<std::string>{ linkEntry.Item.Value == "<LINK_GROUP>" | 
|  | ? groupFeature.Prefix | 
|  | : groupFeature.Suffix, | 
|  | linkEntry.Item.Backtrace }, | 
|  | ItemIsPath::No); | 
|  | } | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (currentFeature && linkEntry.Feature != currentFeature->Name) { | 
|  | // emit feature suffix, if any | 
|  | if (!currentFeature->Suffix.empty()) { | 
|  | this->Items.emplace_back( | 
|  | BT<std::string>{ currentFeature->Suffix, | 
|  | this->Items.back().Value.Backtrace }, | 
|  | ItemIsPath::No); | 
|  | } | 
|  | currentFeature = nullptr; | 
|  | } | 
|  |  | 
|  | if (linkEntry.Feature != DEFAULT && | 
|  | (!currentFeature || linkEntry.Feature != currentFeature->Name)) { | 
|  | if (!this->AddLibraryFeature(linkEntry.Feature)) { | 
|  | continue; | 
|  | } | 
|  | currentFeature = this->FindLibraryFeature(linkEntry.Feature); | 
|  | // emit feature prefix, if any | 
|  | if (!currentFeature->Prefix.empty()) { | 
|  | this->Items.emplace_back( | 
|  | BT<std::string>{ currentFeature->Prefix, linkEntry.Item.Backtrace }, | 
|  | ItemIsPath::No); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (linkEntry.Kind == cmComputeLinkDepends::LinkEntry::SharedDep) { | 
|  | this->AddSharedDepItem(linkEntry); | 
|  | } else { | 
|  | this->AddItem(linkEntry); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (currentFeature) { | 
|  | // emit feature suffix, if any | 
|  | if (!currentFeature->Suffix.empty()) { | 
|  | this->Items.emplace_back( | 
|  | BT<std::string>{ currentFeature->Suffix, | 
|  | this->Items.back().Value.Backtrace }, | 
|  | ItemIsPath::No); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Restore the target link type so the correct system runtime | 
|  | // libraries are found. | 
|  | cmValue lss = this->Target->GetProperty("LINK_SEARCH_END_STATIC"); | 
|  | if (lss.IsOn()) { | 
|  | this->SetCurrentLinkType(LinkStatic); | 
|  | } else { | 
|  | this->SetCurrentLinkType(this->StartLinkType); | 
|  | } | 
|  |  | 
|  | // Add implicit language runtime libraries and directories. | 
|  | this->AddImplicitLinkInfo(); | 
|  |  | 
|  | // Record targets referenced by $<TARGET_OBJECTS:...> sources. | 
|  | this->AddExternalObjectTargets(); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | void FinalizeFeatureFormat(std::string& format, std::string const& activeTag, | 
|  | std::string const& otherTag) | 
|  | { | 
|  | auto pos = format.find(otherTag); | 
|  | if (pos != std::string::npos) { | 
|  | format.erase(pos, format.find('}', pos) - pos + 1); | 
|  | } | 
|  | pos = format.find(activeTag); | 
|  | if (pos != std::string::npos) { | 
|  | format.erase(pos, activeTag.length()); | 
|  | pos = format.find('}', pos); | 
|  | if (pos != std::string::npos) { | 
|  | format.erase(pos, 1); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool IsValidFeatureFormat(std::string const& format) | 
|  | { | 
|  | return format.find("<LIBRARY>") != std::string::npos || | 
|  | format.find("<LIB_ITEM>") != std::string::npos || | 
|  | format.find("<LINK_ITEM>") != std::string::npos; | 
|  | } | 
|  |  | 
|  | class FeaturePlaceHolderExpander : public cmPlaceholderExpander | 
|  | { | 
|  | public: | 
|  | FeaturePlaceHolderExpander(std::string const* library, | 
|  | std::string const* libItem = nullptr, | 
|  | std::string const* linkItem = nullptr) | 
|  | : Library(library) | 
|  | , LibItem(libItem) | 
|  | , LinkItem(linkItem) | 
|  | { | 
|  | } | 
|  |  | 
|  | private: | 
|  | std::string ExpandVariable(std::string const& variable) override | 
|  | { | 
|  | if (this->Library && variable == "LIBRARY") { | 
|  | return *this->Library; | 
|  | } | 
|  | if (this->LibItem && variable == "LIB_ITEM") { | 
|  | return *this->LibItem; | 
|  | } | 
|  | if (this->LinkItem && variable == "LINK_ITEM") { | 
|  | return *this->LinkItem; | 
|  | } | 
|  |  | 
|  | return variable; | 
|  | } | 
|  |  | 
|  | std::string const* Library = nullptr; | 
|  | std::string const* LibItem = nullptr; | 
|  | std::string const* LinkItem = nullptr; | 
|  | }; | 
|  | } | 
|  |  | 
|  | cmComputeLinkInformation::FeatureDescriptor::FeatureDescriptor( | 
|  | std::string name, std::string itemFormat) | 
|  | : Name(std::move(name)) | 
|  | , Supported(true) | 
|  | , ItemPathFormat(std::move(itemFormat)) | 
|  | , ItemNameFormat(this->ItemPathFormat) | 
|  | { | 
|  | } | 
|  | cmComputeLinkInformation::FeatureDescriptor::FeatureDescriptor( | 
|  | std::string name, std::string itemPathFormat, std::string itemNameFormat) | 
|  | : Name(std::move(name)) | 
|  | , Supported(true) | 
|  | , ItemPathFormat(std::move(itemPathFormat)) | 
|  | , ItemNameFormat(std::move(itemNameFormat)) | 
|  | { | 
|  | } | 
|  | cmComputeLinkInformation::FeatureDescriptor::FeatureDescriptor( | 
|  | std::string name, std::string prefix, std::string itemPathFormat, | 
|  | std::string itemNameFormat, std::string suffix) | 
|  | : Name(std::move(name)) | 
|  | , Supported(true) | 
|  | , Prefix(std::move(prefix)) | 
|  | , Suffix(std::move(suffix)) | 
|  | , ItemPathFormat(std::move(itemPathFormat)) | 
|  | , ItemNameFormat(std::move(itemNameFormat)) | 
|  | { | 
|  | } | 
|  | cmComputeLinkInformation::FeatureDescriptor::FeatureDescriptor( | 
|  | std::string name, std::string prefix, std::string suffix, bool) | 
|  | : Name(std::move(name)) | 
|  | , Supported(true) | 
|  | , Prefix(std::move(prefix)) | 
|  | , Suffix(std::move(suffix)) | 
|  | { | 
|  | } | 
|  |  | 
|  | std::string cmComputeLinkInformation::FeatureDescriptor::GetDecoratedItem( | 
|  | std::string const& library, ItemIsPath isPath) const | 
|  | { | 
|  | auto format = | 
|  | isPath == ItemIsPath::Yes ? this->ItemPathFormat : this->ItemNameFormat; | 
|  |  | 
|  | // replace <LIBRARY>, <LIB_ITEM> and <LINK_ITEM> patterns with library path | 
|  | FeaturePlaceHolderExpander expander(&library, &library, &library); | 
|  | return expander.ExpandVariables(format); | 
|  | } | 
|  | std::string cmComputeLinkInformation::FeatureDescriptor::GetDecoratedItem( | 
|  | std::string const& library, std::string const& libItem, | 
|  | std::string const& linkItem, ItemIsPath isPath) const | 
|  | { | 
|  | auto format = | 
|  | isPath == ItemIsPath::Yes ? this->ItemPathFormat : this->ItemNameFormat; | 
|  |  | 
|  | // replace <LIBRARY>, <LIB_ITEM> and <LINK_ITEM> patterns | 
|  | FeaturePlaceHolderExpander expander(&library, &libItem, &linkItem); | 
|  | return expander.ExpandVariables(format); | 
|  | } | 
|  |  | 
|  | cmComputeLinkInformation::LibraryFeatureDescriptor::LibraryFeatureDescriptor( | 
|  | std::string name, std::string itemFormat) | 
|  | : FeatureDescriptor(std::move(name), std::move(itemFormat)) | 
|  | { | 
|  | } | 
|  | cmComputeLinkInformation::LibraryFeatureDescriptor::LibraryFeatureDescriptor( | 
|  | std::string name, std::string itemPathFormat, std::string itemNameFormat) | 
|  | : FeatureDescriptor(std::move(name), std::move(itemPathFormat), | 
|  | std::move(itemNameFormat)) | 
|  | { | 
|  | } | 
|  | cmComputeLinkInformation::LibraryFeatureDescriptor::LibraryFeatureDescriptor( | 
|  | std::string name, std::string prefix, std::string itemPathFormat, | 
|  | std::string itemNameFormat, std::string suffix) | 
|  | : FeatureDescriptor(std::move(name), std::move(prefix), | 
|  | std::move(itemPathFormat), std::move(itemNameFormat), | 
|  | std::move(suffix)) | 
|  | { | 
|  | } | 
|  |  | 
|  | bool cmComputeLinkInformation::AddLibraryFeature(std::string const& feature) | 
|  | { | 
|  | auto it = this->LibraryFeatureDescriptors.find(feature); | 
|  | if (it != this->LibraryFeatureDescriptors.end()) { | 
|  | return it->second.Supported; | 
|  | } | 
|  |  | 
|  | auto featureName = | 
|  | cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_LIBRARY_USING_", feature); | 
|  | cmValue featureSupported = | 
|  | this->Makefile->GetDefinition(cmStrCat(featureName, "_SUPPORTED")); | 
|  | if (!featureSupported) { | 
|  | // language specific variable is not defined, fallback to the more generic | 
|  | // one | 
|  | featureName = cmStrCat("CMAKE_LINK_LIBRARY_USING_", feature); | 
|  | featureSupported = | 
|  | this->Makefile->GetDefinition(cmStrCat(featureName, "_SUPPORTED")); | 
|  | } | 
|  | if (!featureSupported.IsOn()) { | 
|  | this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{}); | 
|  |  | 
|  | this->CMakeInstance->IssueMessage( | 
|  | MessageType::FATAL_ERROR, | 
|  | cmStrCat( | 
|  | "Feature '", feature, | 
|  | "', specified through generator-expression '$<LINK_LIBRARY>' to " | 
|  | "link target '", | 
|  | this->Target->GetName(), "', is not supported for the '", | 
|  | this->LinkLanguage, "' link language."), | 
|  | this->Target->GetBacktrace()); | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | cmValue langFeature = this->Makefile->GetDefinition(featureName); | 
|  | if (!langFeature) { | 
|  | this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{}); | 
|  |  | 
|  | this->CMakeInstance->IssueMessage( | 
|  | MessageType::FATAL_ERROR, | 
|  | cmStrCat( | 
|  | "Feature '", feature, | 
|  | "', specified through generator-expression '$<LINK_LIBRARY>' to " | 
|  | "link target '", | 
|  | this->Target->GetName(), "', is not defined for the '", | 
|  | this->LinkLanguage, "' link language."), | 
|  | this->Target->GetBacktrace()); | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | auto items = cmExpandListWithBacktrace( | 
|  | *langFeature, this->Target->GetBacktrace(), cmList::EmptyElements::Yes); | 
|  |  | 
|  | if ((items.size() == 1 && !IsValidFeatureFormat(items.front().Value)) || | 
|  | (items.size() == 3 && !IsValidFeatureFormat(items[1].Value))) { | 
|  | this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{}); | 
|  | this->CMakeInstance->IssueMessage( | 
|  | MessageType::FATAL_ERROR, | 
|  | cmStrCat("Feature '", feature, "', specified by variable '", featureName, | 
|  | "', is malformed (\"<LIBRARY>\", \"<LIB_ITEM>\", or " | 
|  | "\"<LINK_ITEM>\" patterns " | 
|  | "are missing) and cannot be used to link target '", | 
|  | this->Target->GetName(), "'."), | 
|  | this->Target->GetBacktrace()); | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // now, handle possible "PATH{}" and "NAME{}" patterns | 
|  | if (items.size() == 1) { | 
|  | items.push_back(items.front()); | 
|  | FinalizeFeatureFormat(items[0].Value, "PATH{", "NAME{"); | 
|  | FinalizeFeatureFormat(items[1].Value, "NAME{", "PATH{"); | 
|  | } else if (items.size() == 3) { | 
|  | items.insert(items.begin() + 1, items[1]); | 
|  | FinalizeFeatureFormat(items[1].Value, "PATH{", "NAME{"); | 
|  | FinalizeFeatureFormat(items[2].Value, "NAME{", "PATH{"); | 
|  | } else { | 
|  | this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{}); | 
|  | this->CMakeInstance->IssueMessage( | 
|  | MessageType::FATAL_ERROR, | 
|  | cmStrCat("Feature '", feature, "', specified by variable '", featureName, | 
|  | "', is malformed (wrong number of elements) and cannot be used " | 
|  | "to link target '", | 
|  | this->Target->GetName(), "'."), | 
|  | this->Target->GetBacktrace()); | 
|  |  | 
|  | return false; | 
|  | } | 
|  | if ((items.size() == 2 && !IsValidFeatureFormat(items[0].Value)) || | 
|  | (items.size() == 4 && !IsValidFeatureFormat(items[1].Value))) { | 
|  | // PATH{} has wrong format | 
|  | this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{}); | 
|  | this->CMakeInstance->IssueMessage( | 
|  | MessageType::FATAL_ERROR, | 
|  | cmStrCat("Feature '", feature, "', specified by variable '", featureName, | 
|  | "', is malformed (\"<LIBRARY>\", \"<LIB_ITEM>\", or " | 
|  | "\"<LINK_ITEM>\" patterns " | 
|  | "are missing for \"PATH{}\" alternative) and cannot be used to " | 
|  | "link target '", | 
|  | this->Target->GetName(), "'."), | 
|  | this->Target->GetBacktrace()); | 
|  |  | 
|  | return false; | 
|  | } | 
|  | if ((items.size() == 2 && !IsValidFeatureFormat(items[1].Value)) || | 
|  | (items.size() == 4 && !IsValidFeatureFormat(items[2].Value))) { | 
|  | // NAME{} has wrong format | 
|  | this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{}); | 
|  | this->CMakeInstance->IssueMessage( | 
|  | MessageType::FATAL_ERROR, | 
|  | cmStrCat("Feature '", feature, "', specified by variable '", featureName, | 
|  | "', is malformed (\"<LIBRARY>\", \"<LIB_ITEM>\", or " | 
|  | "\"<LINK_ITEM>\" patterns " | 
|  | "are missing for \"NAME{}\" alternative) and cannot be used to " | 
|  | "link target '", | 
|  | this->Target->GetName(), "'."), | 
|  | this->Target->GetBacktrace()); | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // replace LINKER: pattern | 
|  | this->Target->ResolveLinkerWrapper(items, this->LinkLanguage, true); | 
|  |  | 
|  | if (items.size() == 2) { | 
|  | this->LibraryFeatureDescriptors.emplace( | 
|  | feature, | 
|  | LibraryFeatureDescriptor{ feature, items[0].Value, items[1].Value }); | 
|  | } else { | 
|  | this->LibraryFeatureDescriptors.emplace( | 
|  | feature, | 
|  | LibraryFeatureDescriptor{ feature, items[0].Value, items[1].Value, | 
|  | items[2].Value, items[3].Value }); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | cmComputeLinkInformation::FeatureDescriptor const& | 
|  | cmComputeLinkInformation::GetLibraryFeature(std::string const& feature) const | 
|  | { | 
|  | return this->LibraryFeatureDescriptors.find(feature)->second; | 
|  | } | 
|  | cmComputeLinkInformation::FeatureDescriptor const* | 
|  | cmComputeLinkInformation::FindLibraryFeature(std::string const& feature) const | 
|  | { | 
|  | auto it = this->LibraryFeatureDescriptors.find(feature); | 
|  | if (it == this->LibraryFeatureDescriptors.end()) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | return &it->second; | 
|  | } | 
|  |  | 
|  | cmComputeLinkInformation::GroupFeatureDescriptor::GroupFeatureDescriptor( | 
|  | std::string name, std::string prefix, std::string suffix) | 
|  | : FeatureDescriptor(std::move(name), std::move(prefix), std::move(suffix), | 
|  | true) | 
|  | { | 
|  | } | 
|  |  | 
|  | cmComputeLinkInformation::FeatureDescriptor const& | 
|  | cmComputeLinkInformation::GetGroupFeature(std::string const& feature) | 
|  | { | 
|  | auto it = this->GroupFeatureDescriptors.find(feature); | 
|  | if (it != this->GroupFeatureDescriptors.end()) { | 
|  | return it->second; | 
|  | } | 
|  |  | 
|  | auto featureName = | 
|  | cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_GROUP_USING_", feature); | 
|  | cmValue featureSupported = | 
|  | this->Makefile->GetDefinition(cmStrCat(featureName, "_SUPPORTED")); | 
|  | if (!featureSupported) { | 
|  | // language specific variable is not defined, fallback to the more generic | 
|  | // one | 
|  | featureName = cmStrCat("CMAKE_LINK_GROUP_USING_", feature); | 
|  | featureSupported = | 
|  | this->Makefile->GetDefinition(cmStrCat(featureName, "_SUPPORTED")); | 
|  | } | 
|  | if (!featureSupported.IsOn()) { | 
|  | this->CMakeInstance->IssueMessage( | 
|  | MessageType::FATAL_ERROR, | 
|  | cmStrCat("Feature '", feature, | 
|  | "', specified through generator-expression '$<LINK_GROUP>' to " | 
|  | "link target '", | 
|  | this->Target->GetName(), "', is not supported for the '", | 
|  | this->LinkLanguage, "' link language."), | 
|  | this->Target->GetBacktrace()); | 
|  | return this->GroupFeatureDescriptors.emplace(feature, FeatureDescriptor{}) | 
|  | .first->second; | 
|  | } | 
|  |  | 
|  | cmValue langFeature = this->Makefile->GetDefinition(featureName); | 
|  | if (!langFeature) { | 
|  | this->CMakeInstance->IssueMessage( | 
|  | MessageType::FATAL_ERROR, | 
|  | cmStrCat("Feature '", feature, | 
|  | "', specified through generator-expression '$<LINK_GROUP>' to " | 
|  | "link target '", | 
|  | this->Target->GetName(), "', is not defined for the '", | 
|  | this->LinkLanguage, "' link language."), | 
|  | this->Target->GetBacktrace()); | 
|  | return this->GroupFeatureDescriptors.emplace(feature, FeatureDescriptor{}) | 
|  | .first->second; | 
|  | } | 
|  |  | 
|  | auto items = cmExpandListWithBacktrace( | 
|  | *langFeature, this->Target->GetBacktrace(), cmList::EmptyElements::Yes); | 
|  |  | 
|  | // replace LINKER: pattern | 
|  | this->Target->ResolveLinkerWrapper(items, this->LinkLanguage, true); | 
|  |  | 
|  | if (items.size() == 2) { | 
|  | return this->GroupFeatureDescriptors | 
|  | .emplace( | 
|  | feature, | 
|  | GroupFeatureDescriptor{ feature, items[0].Value, items[1].Value }) | 
|  | .first->second; | 
|  | } | 
|  |  | 
|  | this->CMakeInstance->IssueMessage( | 
|  | MessageType::FATAL_ERROR, | 
|  | cmStrCat("Feature '", feature, "', specified by variable '", featureName, | 
|  | "', is malformed (wrong number of elements) and cannot be used " | 
|  | "to link target '", | 
|  | this->Target->GetName(), "'."), | 
|  | this->Target->GetBacktrace()); | 
|  | return this->GroupFeatureDescriptors.emplace(feature, FeatureDescriptor{}) | 
|  | .first->second; | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::AddExternalObjectTargets() | 
|  | { | 
|  | std::vector<cmSourceFile const*> externalObjects; | 
|  | this->Target->GetExternalObjects(externalObjects, this->Config); | 
|  | std::set<std::string> emitted; | 
|  | for (auto const* externalObject : externalObjects) { | 
|  | std::string const& objLib = externalObject->GetObjectLibrary(); | 
|  | if (objLib.empty()) { | 
|  | continue; | 
|  | } | 
|  | if (emitted.insert(objLib).second) { | 
|  | cmLinkItem const& objItem = | 
|  | this->Target->ResolveLinkItem(BT<std::string>(objLib)); | 
|  | if (objItem.Target) { | 
|  | this->ExternalObjectTargets.emplace_back(objItem.Target); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::AddImplicitLinkInfo() | 
|  | { | 
|  | // The link closure lists all languages whose implicit info is needed. | 
|  | cmGeneratorTarget::LinkClosure const* lc = | 
|  | this->Target->GetLinkClosure(this->Config); | 
|  | for (std::string const& li : lc->Languages) { | 
|  |  | 
|  | if (li == "CUDA" || li == "HIP") { | 
|  | // These need to go before the other implicit link information | 
|  | // as they could require symbols from those other library | 
|  | // Currently restricted as CUDA and HIP are the only languages | 
|  | // we have documented runtime behavior controls for | 
|  | this->AddRuntimeLinkLibrary(li); | 
|  | } | 
|  |  | 
|  | // Skip those of the linker language.  They are implicit. | 
|  | if (li != this->LinkLanguage) { | 
|  | this->AddImplicitLinkInfo(li); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::AddRuntimeLinkLibrary(std::string const& lang) | 
|  | { | 
|  | std::string const& runtimeLibrary = | 
|  | this->Target->GetRuntimeLinkLibrary(lang, this->Config); | 
|  | if (runtimeLibrary.empty()) { | 
|  | return; | 
|  | } | 
|  | if (cmValue runtimeLinkOptions = this->Makefile->GetDefinition(cmStrCat( | 
|  | "CMAKE_", lang, "_RUNTIME_LIBRARY_LINK_OPTIONS_", runtimeLibrary))) { | 
|  | cmList libs{ *runtimeLinkOptions }; | 
|  | for (auto const& i : libs) { | 
|  | if (!cm::contains(this->ImplicitLinkLibs, i)) { | 
|  | this->AddItem({ i }); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::AddImplicitLinkInfo(std::string const& lang) | 
|  | { | 
|  | // Add libraries for this language that are not implied by the | 
|  | // linker language. | 
|  | std::string libVar = cmStrCat("CMAKE_", lang, "_IMPLICIT_LINK_LIBRARIES"); | 
|  | if (cmValue libs = this->Makefile->GetDefinition(libVar)) { | 
|  | cmList libsList{ *libs }; | 
|  | for (auto const& i : libsList) { | 
|  | if (!cm::contains(this->ImplicitLinkLibs, i)) { | 
|  | this->AddItem({ i }); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Add linker search paths for this language that are not | 
|  | // implied by the linker language. | 
|  | std::string dirVar = cmStrCat("CMAKE_", lang, "_IMPLICIT_LINK_DIRECTORIES"); | 
|  | if (cmValue dirs = this->Makefile->GetDefinition(dirVar)) { | 
|  | cmList dirsList{ *dirs }; | 
|  | this->OrderLinkerSearchPath->AddLanguageDirectories(dirsList); | 
|  | } | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::AddItem(LinkEntry const& entry) | 
|  | { | 
|  | cmGeneratorTarget const* tgt = entry.Target; | 
|  | BT<std::string> const& item = entry.Item; | 
|  |  | 
|  | // Compute the proper name to use to link this library. | 
|  | std::string const& config = this->Config; | 
|  | bool impexe = (tgt && tgt->IsExecutableWithExports()); | 
|  | if (impexe && !tgt->HasImportLibrary(config) && !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 using a special feature. | 
|  | cmStateEnums::ArtifactType artifact = tgt->HasImportLibrary(config) | 
|  | ? cmStateEnums::ImportLibraryArtifact | 
|  | : cmStateEnums::RuntimeBinaryArtifact; | 
|  | std::string exe = tgt->GetFullPath(config, artifact, true); | 
|  | this->Items.emplace_back( | 
|  | BT<std::string>(exe, item.Backtrace), ItemIsPath::Yes, tgt, nullptr, | 
|  | this->FindLibraryFeature(entry.Feature == DEFAULT | 
|  | ? "__CMAKE_LINK_EXECUTABLE" | 
|  | : entry.Feature)); | 
|  | this->Depends.push_back(std::move(exe)); | 
|  | } else if (tgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) { | 
|  | // Add the interface library as an item so it can be considered as part | 
|  | // of COMPATIBLE_INTERFACE_ enforcement.  The generators will ignore | 
|  | // this for the actual link line. | 
|  | this->Items.emplace_back(std::string(), ItemIsPath::No, tgt); | 
|  |  | 
|  | // Also add the item the interface specifies to be used in its place. | 
|  | std::string const& libName = tgt->GetImportedLibName(config); | 
|  | if (!libName.empty()) { | 
|  | this->AddItem(BT<std::string>(libName, item.Backtrace)); | 
|  | } | 
|  | } else if (tgt->GetType() == cmStateEnums::OBJECT_LIBRARY) { | 
|  | this->Items.emplace_back(item, ItemIsPath::No, tgt); | 
|  | } else if (this->GlobalGenerator->IsXcode() && | 
|  | !tgt->GetImportedXcFrameworkPath(config).empty()) { | 
|  | this->Items.emplace_back( | 
|  | tgt->GetImportedXcFrameworkPath(config), ItemIsPath::Yes, tgt, nullptr, | 
|  | this->FindLibraryFeature(entry.Feature == DEFAULT | 
|  | ? "__CMAKE_LINK_XCFRAMEWORK" | 
|  | : entry.Feature)); | 
|  | } else { | 
|  | // Decide whether to use an import library. | 
|  | cmStateEnums::ArtifactType artifact = tgt->HasImportLibrary(config) | 
|  | ? cmStateEnums::ImportLibraryArtifact | 
|  | : cmStateEnums::RuntimeBinaryArtifact; | 
|  |  | 
|  | // Pass the full path to the target file. | 
|  | BT<std::string> lib = BT<std::string>( | 
|  | tgt->GetFullPath(config, artifact, true), item.Backtrace); | 
|  | if (tgt->IsAIX() && cmHasLiteralSuffix(lib.Value, "-NOTFOUND") && | 
|  | artifact == cmStateEnums::ImportLibraryArtifact) { | 
|  | // This is an imported executable on AIX that has ENABLE_EXPORTS | 
|  | // but not IMPORTED_IMPLIB.  CMake used to produce and accept such | 
|  | // imported executables on AIX before we taught it to use linker | 
|  | // import files.  For compatibility, simply skip linking to this | 
|  | // executable as we did before.  It works with runtime linking. | 
|  | return; | 
|  | } | 
|  | if (!this->LinkDependsNoShared || | 
|  | tgt->GetType() != cmStateEnums::SHARED_LIBRARY) { | 
|  | this->Depends.push_back(lib.Value); | 
|  | } | 
|  |  | 
|  | LinkEntry libEntry{ entry }; | 
|  | libEntry.Item = lib; | 
|  | this->AddTargetItem(libEntry); | 
|  | if (tgt->IsApple() && tgt->HasImportLibrary(config)) { | 
|  | // Use the library rather than the tbd file for runpath computation | 
|  | this->AddLibraryRuntimeInfo( | 
|  | tgt->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact, true), | 
|  | tgt); | 
|  | } else { | 
|  | this->AddLibraryRuntimeInfo(lib.Value, tgt); | 
|  | } | 
|  | if (tgt && tgt->GetType() == cmStateEnums::SHARED_LIBRARY && | 
|  | this->Target->IsDLLPlatform()) { | 
|  | this->AddRuntimeDLL(tgt); | 
|  | } | 
|  | } | 
|  |  | 
|  | auto xcFrameworkPath = tgt->GetImportedXcFrameworkPath(config); | 
|  | if (!xcFrameworkPath.empty()) { | 
|  | auto plist = cmParseXcFrameworkPlist(xcFrameworkPath, *this->Makefile, | 
|  | item.Backtrace); | 
|  | if (!plist) { | 
|  | return; | 
|  | } | 
|  | if (auto const* library = | 
|  | plist->SelectSuitableLibrary(*this->Makefile, item.Backtrace)) { | 
|  | if (!library->HeadersPath.empty()) { | 
|  | this->AddXcFrameworkHeaderPath(cmStrCat(xcFrameworkPath, '/', | 
|  | library->LibraryIdentifier, | 
|  | '/', library->HeadersPath)); | 
|  | } | 
|  | } else { | 
|  | return; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | // This is not a CMake target.  Use the name given. | 
|  | if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s) || | 
|  | (entry.Feature == DEFAULT && | 
|  | cmSystemTools::IsPathToFramework(item.Value) && | 
|  | this->Target->IsApple())) { | 
|  | // This is a framework. | 
|  | this->AddFrameworkItem(entry); | 
|  | } else if (cmHasSuffix(entry.Feature, "XCFRAMEWORK"_s) || | 
|  | (entry.Feature == DEFAULT && | 
|  | cmSystemTools::IsPathToXcFramework(item.Value) && | 
|  | this->Target->IsApple())) { | 
|  | // This is a framework. | 
|  | this->AddXcFrameworkItem(entry); | 
|  | } else if (cmSystemTools::FileIsFullPath(item.Value)) { | 
|  | if (cmSystemTools::FileIsDirectory(item.Value)) { | 
|  | // This is a directory. | 
|  | this->DropDirectoryItem(item); | 
|  | } else { | 
|  | // Use the full path given to the library file. | 
|  | this->Depends.push_back(item.Value); | 
|  | this->AddFullItem(entry); | 
|  | this->AddLibraryRuntimeInfo(item.Value); | 
|  | } | 
|  | } else if (entry.Kind != cmComputeLinkDepends::LinkEntry::Object) { | 
|  | // This is a library or option specified by the user. | 
|  | this->AddUserItem(entry); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::AddSharedDepItem(LinkEntry const& entry) | 
|  | { | 
|  | BT<std::string> const& item = entry.Item; | 
|  | cmGeneratorTarget const* tgt = entry.Target; | 
|  |  | 
|  | // Record dependencies on DLLs. | 
|  | if (tgt && tgt->GetType() == cmStateEnums::SHARED_LIBRARY && | 
|  | this->Target->IsDLLPlatform() && | 
|  | this->SharedDependencyMode != SharedDepModeLink) { | 
|  | this->AddRuntimeDLL(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() != cmStateEnums::SHARED_LIBRARY) { | 
|  | return; | 
|  | } | 
|  | } else { | 
|  | // Skip items that are not full paths.  We will not be able to | 
|  | // reliably specify them. | 
|  | if (!cmSystemTools::FileIsFullPath(item.Value)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Get the name of the library from the file name. | 
|  | std::string file = cmSystemTools::GetFilenameName(item.Value); | 
|  | if (!this->ExtractSharedLibraryName.find(file)) { | 
|  | // This is not the name of a shared library. | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | // If in linking mode, just link to the shared library. | 
|  | if (this->SharedDependencyMode == SharedDepModeLink || | 
|  | // For an imported shared library without a known runtime artifact, | 
|  | // such as a CUDA stub, a library file named with the real soname | 
|  | // may not be available at all, so '-rpath-link' cannot help linkers | 
|  | // find it to satisfy '--no-allow-shlib-undefined' recursively. | 
|  | // Pass this dependency to the linker explicitly just in case. | 
|  | // If the linker also uses '--as-needed' behavior, this will not | 
|  | // add an unnecessary direct dependency. | 
|  | (tgt && tgt->IsImported() && | 
|  | !tgt->HasKnownRuntimeArtifactLocation(this->Config) && | 
|  | this->Target->LinkerEnforcesNoAllowShLibUndefined(this->Config))) { | 
|  | this->AddItem(entry); | 
|  | 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) { | 
|  | cmStateEnums::ArtifactType artifact = tgt->HasImportLibrary(this->Config) | 
|  | ? cmStateEnums::ImportLibraryArtifact | 
|  | : cmStateEnums::RuntimeBinaryArtifact; | 
|  | lib = tgt->GetFullPath(this->Config, artifact); | 
|  | if (tgt->IsApple() && tgt->HasImportLibrary(this->Config)) { | 
|  | // Use the library rather than the tbd file for runpath computation | 
|  | this->AddLibraryRuntimeInfo( | 
|  | tgt->GetFullPath(this->Config, cmStateEnums::RuntimeBinaryArtifact, | 
|  | true), | 
|  | tgt); | 
|  | } else { | 
|  | this->AddLibraryRuntimeInfo(lib, tgt); | 
|  | } | 
|  | } else { | 
|  | lib = item.Value; | 
|  | this->AddLibraryRuntimeInfo(lib); | 
|  | } | 
|  |  | 
|  | // Check if we need to include the dependent shared library in other | 
|  | // path ordering. | 
|  | cmOrderDirectories* order = nullptr; | 
|  | if (this->SharedDependencyMode == SharedDepModeLibDir && | 
|  | !this->LinkWithRuntimePath /* AddLibraryRuntimeInfo adds it */) { | 
|  | // Add the item to the linker search path. | 
|  | order = this->OrderLinkerSearchPath.get(); | 
|  | } else if (this->SharedDependencyMode == SharedDepModeDir) { | 
|  | // Add the item to the separate dependent library search path. | 
|  | order = this->OrderDependentRPath.get(); | 
|  | } | 
|  | if (order) { | 
|  | if (tgt) { | 
|  | std::string soName = tgt->GetSOName(this->Config); | 
|  | char const* soname = soName.empty() ? nullptr : soName.c_str(); | 
|  | order->AddRuntimeLibrary(lib, soname); | 
|  | } else { | 
|  | order->AddRuntimeLibrary(lib); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::AddRuntimeDLL(cmGeneratorTarget const* tgt) | 
|  | { | 
|  | if (std::find(this->RuntimeDLLs.begin(), this->RuntimeDLLs.end(), tgt) == | 
|  | this->RuntimeDLLs.end()) { | 
|  | this->RuntimeDLLs.emplace_back(tgt); | 
|  | } | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::ComputeLinkTypeInfo() | 
|  | { | 
|  | // Check whether archives may actually be shared libraries. | 
|  | this->ArchivesMayBeShared = | 
|  | this->CMakeInstance->GetState()->GetGlobalPropertyAsBool( | 
|  | "TARGET_ARCHIVES_MAY_BE_SHARED_LIBS"); | 
|  |  | 
|  | // First assume we cannot do link type stuff. | 
|  | this->LinkTypeEnabled = false; | 
|  |  | 
|  | // Lookup link type selection flags. | 
|  | cmValue static_link_type_flag = nullptr; | 
|  | cmValue shared_link_type_flag = nullptr; | 
|  | char const* target_type_str = nullptr; | 
|  | switch (this->Target->GetType()) { | 
|  | case cmStateEnums::EXECUTABLE: | 
|  | target_type_str = "EXE"; | 
|  | break; | 
|  | case cmStateEnums::SHARED_LIBRARY: | 
|  | target_type_str = "SHARED_LIBRARY"; | 
|  | break; | 
|  | case cmStateEnums::MODULE_LIBRARY: | 
|  | target_type_str = "SHARED_MODULE"; | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | if (target_type_str) { | 
|  | std::string static_link_type_flag_var = | 
|  | cmStrCat("CMAKE_", target_type_str, "_LINK_STATIC_", this->LinkLanguage, | 
|  | "_FLAGS"); | 
|  | static_link_type_flag = | 
|  | this->Makefile->GetDefinition(static_link_type_flag_var); | 
|  |  | 
|  | std::string shared_link_type_flag_var = | 
|  | cmStrCat("CMAKE_", target_type_str, "_LINK_DYNAMIC_", this->LinkLanguage, | 
|  | "_FLAGS"); | 
|  | shared_link_type_flag = | 
|  | this->Makefile->GetDefinition(shared_link_type_flag_var); | 
|  | } | 
|  |  | 
|  | // We can support link type switching only if all needed flags are | 
|  | // known. | 
|  | if (cmNonempty(static_link_type_flag) && cmNonempty(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?). | 
|  | cmValue lss = this->Target->GetProperty("LINK_SEARCH_START_STATIC"); | 
|  | this->StartLinkType = lss.IsOn() ? LinkStatic : LinkShared; | 
|  | this->CurrentLinkType = this->StartLinkType; | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::ComputeItemParserInfo() | 
|  | { | 
|  | // Get possible library name prefixes. | 
|  | cmMakefile* mf = this->Makefile; | 
|  | this->AddLinkPrefix(mf->GetSafeDefinition("CMAKE_STATIC_LIBRARY_PREFIX")); | 
|  | this->AddLinkPrefix(mf->GetSafeDefinition("CMAKE_SHARED_LIBRARY_PREFIX")); | 
|  |  | 
|  | // Import library names should be matched and treated as shared | 
|  | // libraries for the purposes of linking. | 
|  | this->AddLinkExtension(mf->GetSafeDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX"), | 
|  | LinkShared); | 
|  | this->AddLinkExtension(mf->GetSafeDefinition("CMAKE_STATIC_LIBRARY_SUFFIX"), | 
|  | LinkStatic); | 
|  | this->AddLinkExtension(mf->GetSafeDefinition("CMAKE_SHARED_LIBRARY_SUFFIX"), | 
|  | LinkShared); | 
|  | this->AddLinkExtension(mf->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX"), | 
|  | LinkUnknown); | 
|  | if (cmValue linkSuffixes = | 
|  | mf->GetDefinition("CMAKE_EXTRA_LINK_EXTENSIONS")) { | 
|  | cmList linkSuffixList{ *linkSuffixes }; | 
|  | for (auto const& i : linkSuffixList) { | 
|  | this->AddLinkExtension(i, LinkUnknown); | 
|  | } | 
|  | } | 
|  | if (cmValue sharedSuffixes = | 
|  | mf->GetDefinition("CMAKE_EXTRA_SHARED_LIBRARY_SUFFIXES")) { | 
|  | cmList sharedSuffixList{ *sharedSuffixes }; | 
|  | for (std::string const& i : sharedSuffixList) { | 
|  | this->AddLinkExtension(i, LinkShared); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Compute a regex to match link extensions. | 
|  | std::string libext = | 
|  | this->CreateExtensionRegex(this->LinkExtensions, LinkUnknown); | 
|  |  | 
|  | // 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::string const& p : this->LinkPrefixes) { | 
|  | reg += p; | 
|  | reg += '|'; | 
|  | } | 
|  | reg += ")([^/:]*)"; | 
|  |  | 
|  | // Create a regex to match any library name. | 
|  | std::string reg_any = cmStrCat(reg, libext); | 
|  | #ifdef CM_COMPUTE_LINK_INFO_DEBUG | 
|  | fprintf(stderr, "any regex [%s]\n", reg_any.c_str()); | 
|  | #endif | 
|  | this->ExtractAnyLibraryName.compile(reg_any); | 
|  |  | 
|  | // Create a regex to match static library names. | 
|  | if (!this->StaticLinkExtensions.empty()) { | 
|  | std::string reg_static = cmStrCat( | 
|  | reg, this->CreateExtensionRegex(this->StaticLinkExtensions, LinkStatic)); | 
|  | #ifdef CM_COMPUTE_LINK_INFO_DEBUG | 
|  | fprintf(stderr, "static regex [%s]\n", reg_static.c_str()); | 
|  | #endif | 
|  | this->ExtractStaticLibraryName.compile(reg_static); | 
|  | } | 
|  |  | 
|  | // Create a regex to match shared library names. | 
|  | if (!this->SharedLinkExtensions.empty()) { | 
|  | std::string reg_shared = reg; | 
|  | this->SharedRegexString = | 
|  | this->CreateExtensionRegex(this->SharedLinkExtensions, LinkShared); | 
|  | 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); | 
|  | } | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::AddLinkPrefix(std::string const& p) | 
|  | { | 
|  | if (!p.empty()) { | 
|  | this->LinkPrefixes.insert(p); | 
|  | } | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::AddLinkExtension(std::string const& e, | 
|  | LinkType type) | 
|  | { | 
|  | if (!e.empty()) { | 
|  | if (type == LinkStatic) { | 
|  | this->StaticLinkExtensions.emplace_back(e); | 
|  | } | 
|  | if (type == LinkShared) { | 
|  | this->SharedLinkExtensions.emplace_back(e); | 
|  | } | 
|  | this->LinkExtensions.emplace_back(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | // XXX(clang-tidy): This method's const-ness is platform dependent, so we | 
|  | // cannot make it `const` as `clang-tidy` wants us to. | 
|  | // NOLINTNEXTLINE(readability-make-member-function-const) | 
|  | std::string cmComputeLinkInformation::CreateExtensionRegex( | 
|  | std::vector<std::string> const& exts, LinkType type) | 
|  | { | 
|  | // Build a list of extension choices. | 
|  | std::string libext = "("; | 
|  | char const* sep = ""; | 
|  | for (std::string const& i : exts) { | 
|  | // 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); | 
|  | #else | 
|  | libext += i; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | // Finish the list. | 
|  | libext += ')'; | 
|  |  | 
|  | // Add an optional OpenBSD-style version or major.minor.version component. | 
|  | if (this->IsOpenBSD || type == LinkShared) { | 
|  | libext += "(\\.[0-9]+)*"; | 
|  | } | 
|  |  | 
|  | libext += '$'; | 
|  | return libext; | 
|  | } | 
|  |  | 
|  | std::string cmComputeLinkInformation::NoCaseExpression(std::string const& str) | 
|  | { | 
|  | std::string ret; | 
|  | ret.reserve(str.size() * 4); | 
|  | for (char c : str) { | 
|  | if (c == '.') { | 
|  | ret += c; | 
|  | } else { | 
|  | ret += '['; | 
|  | ret += static_cast<char>(tolower(c)); | 
|  | ret += static_cast<char>(toupper(c)); | 
|  | ret += ']'; | 
|  | } | 
|  | } | 
|  | 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.emplace_back(this->StaticLinkTypeFlag, ItemIsPath::No); | 
|  | break; | 
|  | case LinkShared: | 
|  | this->Items.emplace_back(this->SharedLinkTypeFlag, ItemIsPath::No); | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry) | 
|  | { | 
|  | // 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. | 
|  | BT<std::string> const& item = entry.Item; | 
|  | cmGeneratorTarget const* target = entry.Target; | 
|  |  | 
|  | if (target->GetType() != cmStateEnums::STATIC_LIBRARY) { | 
|  | this->SetCurrentLinkType(LinkShared); | 
|  | } | 
|  |  | 
|  | // Keep track of shared library targets linked. | 
|  | if (target->GetType() == cmStateEnums::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(entry); | 
|  | return; | 
|  | } | 
|  |  | 
|  | bool const isImportedFrameworkFolderOnApple = | 
|  | target->IsImportedFrameworkFolderOnApple(this->Config); | 
|  | if (target->IsFrameworkOnApple() || isImportedFrameworkFolderOnApple) { | 
|  | // Add the framework directory and the framework item itself | 
|  | auto fwDescriptor = this->GlobalGenerator->SplitFrameworkPath( | 
|  | item.Value, cmGlobalGenerator::FrameworkFormat::Extended); | 
|  | if (!fwDescriptor) { | 
|  | this->CMakeInstance->IssueMessage( | 
|  | MessageType::FATAL_ERROR, | 
|  | cmStrCat("Could not parse framework path \"", item.Value, | 
|  | "\" linked by target ", this->Target->GetName(), '.'), | 
|  | item.Backtrace); | 
|  | return; | 
|  | } | 
|  | if (!fwDescriptor->Directory.empty()) { | 
|  | // Add the directory portion to the framework search path. | 
|  | this->AddFrameworkPath(fwDescriptor->Directory); | 
|  | } | 
|  |  | 
|  | if (this->GlobalGenerator->IsXcode()) { | 
|  | if (isImportedFrameworkFolderOnApple) { | 
|  | if (entry.Feature == DEFAULT) { | 
|  | this->AddLibraryFeature("FRAMEWORK"); | 
|  | this->Items.emplace_back(item, ItemIsPath::Yes, target, nullptr, | 
|  | this->FindLibraryFeature("FRAMEWORK")); | 
|  | } else { | 
|  | this->Items.emplace_back(item, ItemIsPath::Yes, target, nullptr, | 
|  | this->FindLibraryFeature(entry.Feature)); | 
|  | } | 
|  | } else { | 
|  | this->Items.emplace_back( | 
|  | item, ItemIsPath::Yes, target, nullptr, | 
|  | this->FindLibraryFeature(entry.Feature == DEFAULT | 
|  | ? "__CMAKE_LINK_FRAMEWORK" | 
|  | : entry.Feature)); | 
|  | } | 
|  | } else { | 
|  | if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s)) { | 
|  | this->Items.emplace_back(fwDescriptor->GetLinkName(), ItemIsPath::Yes, | 
|  | target, nullptr, | 
|  | this->FindLibraryFeature(entry.Feature)); | 
|  | } else if (entry.Feature == DEFAULT && | 
|  | isImportedFrameworkFolderOnApple) { | 
|  | this->AddLibraryFeature("FRAMEWORK"); | 
|  | this->Items.emplace_back(fwDescriptor->GetLinkName(), ItemIsPath::Yes, | 
|  | target, nullptr, | 
|  | this->FindLibraryFeature("FRAMEWORK")); | 
|  | } else { | 
|  | this->Items.emplace_back( | 
|  | item, ItemIsPath::Yes, target, nullptr, | 
|  | this->FindLibraryFeature(entry.Feature == DEFAULT | 
|  | ? "__CMAKE_LINK_LIBRARY" | 
|  | : entry.Feature)); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | // Now add the full path to the library. | 
|  | this->Items.emplace_back( | 
|  | item, ItemIsPath::Yes, target, nullptr, | 
|  | this->FindLibraryFeature( | 
|  | entry.Feature == DEFAULT ? "__CMAKE_LINK_LIBRARY" : entry.Feature)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::AddFullItem(LinkEntry const& entry) | 
|  | { | 
|  | BT<std::string> const& item = entry.Item; | 
|  |  | 
|  | // Check for the implicit link directory special case. | 
|  | if (this->CheckImplicitDirItem(entry)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Check for case of shared library with no builtin soname. | 
|  | if (this->NoSONameUsesPath && this->CheckSharedLibNoSOName(entry)) { | 
|  | 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.Value); | 
|  | if (this->ExtractSharedLibraryName.find(name)) { | 
|  | this->SetCurrentLinkType(LinkShared); | 
|  | } else if (!this->ExtractStaticLibraryName.find(item.Value)) { | 
|  | // We cannot determine the type.  Assume it is the target's | 
|  | // default type. | 
|  | this->SetCurrentLinkType(this->StartLinkType); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Now add the full path to the library. | 
|  | this->Items.emplace_back( | 
|  | item, ItemIsPath::Yes, nullptr, entry.ObjectSource, | 
|  | this->FindLibraryFeature( | 
|  | entry.Feature == DEFAULT | 
|  | ? (entry.Kind == cmComputeLinkDepends::LinkEntry::Object | 
|  | ? "__CMAKE_LINK_OBJECT" | 
|  | : "__CMAKE_LINK_LIBRARY") | 
|  | : entry.Feature)); | 
|  | } | 
|  |  | 
|  | bool cmComputeLinkInformation::CheckImplicitDirItem(LinkEntry const& entry) | 
|  | { | 
|  | BT<std::string> const& item = entry.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.Value); | 
|  | if (!cm::contains(this->ImplicitLinkDirs, dir)) { | 
|  | // 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.Value); | 
|  | if (!this->ExtractAnyLibraryName.find(file)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::AddUserItem(LinkEntry const& entry) | 
|  | { | 
|  | // 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 | 
|  |  | 
|  | cm::string_view const LINKER{ "LINKER:" }; | 
|  | BT<std::string> const& item = entry.Item; | 
|  |  | 
|  | if (item.Value[0] == '-' || item.Value[0] == '$' || item.Value[0] == '`') { | 
|  | // Pass flags through untouched. | 
|  |  | 
|  | // Restore the target link type since this item does not specify | 
|  | // one. | 
|  | this->SetCurrentLinkType(this->StartLinkType); | 
|  |  | 
|  | // Use the item verbatim. | 
|  | this->Items.emplace_back(item, ItemIsPath::No); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (cmHasPrefix(item.Value, LINKER)) { | 
|  | std::vector<BT<std::string>> linkerFlag{ 1, item }; | 
|  | this->Target->ResolveLinkerWrapper(linkerFlag, this->GetLinkLanguage(), | 
|  | /* joinItems = */ true); | 
|  | if (!linkerFlag.empty()) { | 
|  | this->Items.emplace_back(linkerFlag.front(), ItemIsPath::No); | 
|  | } | 
|  | 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.Value)) { | 
|  | // 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.Value)) { | 
|  | // 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.Value)) { | 
|  | // 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 { | 
|  | // 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.Value; | 
|  | } | 
|  |  | 
|  | // Create an option to ask the linker to search for the library. | 
|  | auto out = cmStrCat(this->LibLinkFlag, lib, this->LibLinkSuffix); | 
|  |  | 
|  | if (entry.Feature != DEFAULT) { | 
|  | auto const& feature = this->GetLibraryFeature(entry.Feature); | 
|  | this->Items.emplace_back( | 
|  | BT<std::string>( | 
|  | feature.GetDecoratedItem(cmStrCat(lib, this->LibLinkSuffix), | 
|  | item.Value, out, ItemIsPath::No), | 
|  | item.Backtrace), | 
|  | ItemIsPath::No); | 
|  | } else { | 
|  | this->Items.emplace_back(BT<std::string>(out, item.Backtrace), | 
|  | ItemIsPath::No); | 
|  | } | 
|  |  | 
|  | // 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(LinkEntry const& entry) | 
|  | { | 
|  | std::string const& item = entry.Item.Value; | 
|  |  | 
|  | // Try to separate the framework name and path. | 
|  | auto fwDescriptor = this->GlobalGenerator->SplitFrameworkPath( | 
|  | item, | 
|  | entry.Feature == DEFAULT ? cmGlobalGenerator::FrameworkFormat::Relaxed | 
|  | : cmGlobalGenerator::FrameworkFormat::Extended); | 
|  | if (!fwDescriptor) { | 
|  | std::ostringstream e; | 
|  | e << "Could not parse framework path \"" << item << "\" linked by target " | 
|  | << this->Target->GetName() << '.'; | 
|  | cmSystemTools::Error(e.str()); | 
|  | return; | 
|  | } | 
|  |  | 
|  | std::string const& fw_path = fwDescriptor->Directory; | 
|  | if (!fw_path.empty()) { | 
|  | // Add the directory portion to the framework search path. | 
|  | this->AddFrameworkPath(fw_path); | 
|  | } | 
|  |  | 
|  | // add runtime information | 
|  | this->AddLibraryRuntimeInfo(fwDescriptor->GetFullPath()); | 
|  |  | 
|  | if (entry.Feature == DEFAULT) { | 
|  | // ensure FRAMEWORK feature is loaded | 
|  | this->AddLibraryFeature("FRAMEWORK"); | 
|  | } | 
|  |  | 
|  | if (this->GlobalGenerator->IsXcode()) { | 
|  | // Add framework path - it will be handled by Xcode after it's added to | 
|  | // "Link Binary With Libraries" build phase | 
|  | this->Items.emplace_back(item, ItemIsPath::Yes, nullptr, nullptr, | 
|  | this->FindLibraryFeature(entry.Feature == DEFAULT | 
|  | ? "FRAMEWORK" | 
|  | : entry.Feature)); | 
|  | } else { | 
|  | this->Items.emplace_back( | 
|  | fwDescriptor->GetLinkName(), ItemIsPath::Yes, nullptr, nullptr, | 
|  | this->FindLibraryFeature(entry.Feature == DEFAULT ? "FRAMEWORK" | 
|  | : entry.Feature)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::AddXcFrameworkItem(LinkEntry const& entry) | 
|  | { | 
|  | auto plist = cmParseXcFrameworkPlist(entry.Item.Value, *this->Makefile, | 
|  | entry.Item.Backtrace); | 
|  | if (!plist) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (auto const* lib = | 
|  | plist->SelectSuitableLibrary(*this->Makefile, entry.Item.Backtrace)) { | 
|  | if (this->GlobalGenerator->IsXcode()) { | 
|  | this->Items.emplace_back( | 
|  | entry.Item.Value, ItemIsPath::Yes, nullptr, nullptr, | 
|  | this->FindLibraryFeature(entry.Feature == DEFAULT | 
|  | ? "__CMAKE_LINK_XCFRAMEWORK" | 
|  | : entry.Feature)); | 
|  | } else { | 
|  | auto libraryPath = cmStrCat( | 
|  | entry.Item.Value, '/', lib->LibraryIdentifier, '/', lib->LibraryPath); | 
|  | LinkEntry libraryEntry( | 
|  | BT<std::string>(libraryPath, entry.Item.Backtrace), entry.Target); | 
|  |  | 
|  | if (cmSystemTools::IsPathToFramework(libraryPath) && | 
|  | this->Target->IsApple()) { | 
|  | // This is a framework. | 
|  | this->AddFrameworkItem(libraryEntry); | 
|  | } else { | 
|  | this->Depends.push_back(libraryPath); | 
|  | this->AddFullItem(libraryEntry); | 
|  | this->AddLibraryRuntimeInfo(libraryPath); | 
|  | if (!lib->HeadersPath.empty()) { | 
|  | this->AddXcFrameworkHeaderPath(cmStrCat(entry.Item.Value, '/', | 
|  | lib->LibraryIdentifier, '/', | 
|  | lib->HeadersPath)); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::DropDirectoryItem(BT<std::string> const& item) | 
|  | { | 
|  | // A full path to a directory was found as a link item.  Warn the | 
|  | // user. | 
|  | this->CMakeInstance->IssueMessage( | 
|  | MessageType::WARNING, | 
|  | cmStrCat("Target \"", this->Target->GetName(), | 
|  | "\" requests linking to directory \"", item.Value, | 
|  | "\".  Targets may link only to libraries.  CMake is dropping " | 
|  | "the item."), | 
|  | item.Backtrace); | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::ComputeFrameworkInfo() | 
|  | { | 
|  | // Avoid adding implicit framework paths. | 
|  | cmList implicitDirs; | 
|  |  | 
|  | // Get platform-wide implicit directories. | 
|  | implicitDirs.assign(this->Makefile->GetDefinition( | 
|  | "CMAKE_PLATFORM_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES")); | 
|  |  | 
|  | // Get language-specific implicit directories. | 
|  | std::string implicitDirVar = cmStrCat( | 
|  | "CMAKE_", this->LinkLanguage, "_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES"); | 
|  | implicitDirs.append(this->Makefile->GetDefinition(implicitDirVar)); | 
|  |  | 
|  | this->FrameworkPathsEmitted.insert(implicitDirs.begin(), implicitDirs.end()); | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::AddFrameworkPath(std::string const& p) | 
|  | { | 
|  | if (this->FrameworkPathsEmitted.insert(p).second) { | 
|  | this->FrameworkPaths.push_back(p); | 
|  | } | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::AddXcFrameworkHeaderPath(std::string const& p) | 
|  | { | 
|  | this->XcFrameworkHeaderPaths.push_back(p); | 
|  | } | 
|  |  | 
|  | bool cmComputeLinkInformation::CheckSharedLibNoSOName(LinkEntry const& entry) | 
|  | { | 
|  | // 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(entry.Item.Value); | 
|  | 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(entry.Item.Value, soname)) { | 
|  | this->AddSharedLibNoSOName(entry); | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::AddSharedLibNoSOName(LinkEntry const& entry) | 
|  | { | 
|  | // 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. | 
|  | LinkEntry fileEntry{ entry }; | 
|  | fileEntry.Item = cmSystemTools::GetFilenameName(entry.Item.Value); | 
|  | this->AddUserItem(fileEntry); | 
|  |  | 
|  | // Make sure the link directory ordering will find the library. | 
|  | this->OrderLinkerSearchPath->AddLinkLibrary(entry.Item.Value); | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::LoadImplicitLinkInfo() | 
|  | { | 
|  | // Get platform-wide implicit directories. | 
|  | cmList implicitDirs{ this->Makefile->GetDefinition( | 
|  | "CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES") }; | 
|  |  | 
|  | // Append library architecture to all implicit platform directories | 
|  | // and add them to the set | 
|  | if (cmValue libraryArch = | 
|  | this->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE")) { | 
|  | for (auto const& i : implicitDirs) { | 
|  | this->ImplicitLinkDirs.insert(cmStrCat(i, '/', *libraryArch)); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Get language-specific implicit directories. | 
|  | std::string implicitDirVar = | 
|  | cmStrCat("CMAKE_", this->LinkLanguage, "_IMPLICIT_LINK_DIRECTORIES"); | 
|  | implicitDirs.append(this->Makefile->GetDefinition(implicitDirVar)); | 
|  |  | 
|  | // Store implicit link directories. | 
|  | this->ImplicitLinkDirs.insert(implicitDirs.begin(), implicitDirs.end()); | 
|  |  | 
|  | // Get language-specific implicit libraries. | 
|  | std::string implicitLibVar = | 
|  | cmStrCat("CMAKE_", this->LinkLanguage, "_IMPLICIT_LINK_LIBRARIES"); | 
|  | cmList implicitLibs{ this->Makefile->GetDefinition(implicitLibVar) }; | 
|  |  | 
|  | // Store implicit link libraries. | 
|  | for (auto const& item : implicitLibs) { | 
|  | // Items starting in '-' but not '-l' are flags, not libraries, | 
|  | // and should not be filtered by this implicit list. | 
|  | if (item[0] != '-' || item[1] == 'l') { | 
|  | this->ImplicitLinkLibs.insert(item); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Get platform specific rpath link directories | 
|  | cmList::append(this->RuntimeLinkDirs, | 
|  | this->Makefile->GetDefinition("CMAKE_PLATFORM_RUNTIME_PATH")); | 
|  | } | 
|  |  | 
|  | std::vector<std::string> const& | 
|  | cmComputeLinkInformation::GetRuntimeSearchPath() const | 
|  | { | 
|  | return this->OrderRuntimeSearchPath->GetOrderedDirectories(); | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::AddLibraryRuntimeInfo( | 
|  | std::string const& fullPath, cmGeneratorTarget const* target) | 
|  | { | 
|  | // Ignore targets on Apple where install_name is not @rpath. | 
|  | // The dependenty library can be found with other means such as | 
|  | // @loader_path or full paths. | 
|  | if (this->Makefile->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) { | 
|  | if (!target->HasMacOSXRpathInstallNameDir(this->Config)) { | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Libraries with unknown type must be handled using just the file | 
|  | // on disk. | 
|  | if (target->GetType() == cmStateEnums::UNKNOWN_LIBRARY) { | 
|  | this->AddLibraryRuntimeInfo(fullPath); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Skip targets that are not shared libraries (modules cannot be linked). | 
|  | if (target->GetType() != cmStateEnums::SHARED_LIBRARY) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Skip targets that do not have a known runtime artifact. | 
|  | if (!target->HasKnownRuntimeArtifactLocation(this->Config)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Try to get the soname of the library.  Only files with this name | 
|  | // could possibly conflict. | 
|  | std::string soName = target->GetSOName(this->Config); | 
|  | char const* soname = soName.empty() ? nullptr : 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. | 
|  | bool is_shared_library = false; | 
|  | std::string file = cmSystemTools::GetFilenameName(fullPath); | 
|  |  | 
|  | if (this->Makefile->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) { | 
|  | // Check that @rpath is part of the install name. | 
|  | // If it isn't, return. | 
|  | std::string soname; | 
|  | if (!cmSystemTools::GuessLibraryInstallName(fullPath, soname)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (soname.find("@rpath") == std::string::npos) { | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | is_shared_library = this->ExtractSharedLibraryName.find(file); | 
|  |  | 
|  | if (!is_shared_library) { | 
|  | // On some platforms (AIX) a shared library may look static. | 
|  | if (this->ArchivesMayBeShared) { | 
|  | if (this->ExtractStaticLibraryName.find(file)) { | 
|  | // This is the name of a shared library or archive. | 
|  | is_shared_library = true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // It could be an Apple framework | 
|  | if (!is_shared_library) { | 
|  | is_shared_library = | 
|  | this->GlobalGenerator | 
|  | ->SplitFrameworkPath(fullPath, | 
|  | cmGlobalGenerator::FrameworkFormat::Strict) | 
|  | .has_value(); | 
|  | } | 
|  |  | 
|  | if (!is_shared_library) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Include this library in the runtime path ordering. | 
|  | this->OrderRuntimeSearchPath->AddRuntimeLibrary(fullPath); | 
|  | if (this->LinkWithRuntimePath) { | 
|  | this->OrderLinkerSearchPath->AddRuntimeLibrary(fullPath); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void cmCLI_ExpandListUnique(std::string const& str, | 
|  | std::vector<std::string>& out, | 
|  | std::set<std::string>& emitted) | 
|  | { | 
|  | cmList tmp{ str }; | 
|  | for (std::string const& i : tmp) { | 
|  | if (emitted.insert(i).second) { | 
|  | out.push_back(i); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs, | 
|  | bool for_install) const | 
|  | { | 
|  | // 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(this->Config) && | 
|  | linking_for_install); | 
|  | bool use_build_rpath = | 
|  | (outputRuntime && this->Target->HaveBuildTreeRPATH(this->Config) && | 
|  | !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"); | 
|  |  | 
|  | // Select whether to use $ORIGIN in RPATHs for artifacts in the build tree. | 
|  | std::string const& originToken = this->Makefile->GetSafeDefinition( | 
|  | "CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN"); | 
|  | std::string targetOutputDir = this->Target->GetDirectory(this->Config); | 
|  | bool use_relative_build_rpath = | 
|  | this->Target->GetPropertyAsBool("BUILD_RPATH_USE_ORIGIN") && | 
|  | !originToken.empty() && !targetOutputDir.empty(); | 
|  |  | 
|  | // Construct the RPATH. | 
|  | std::set<std::string> emitted; | 
|  | if (use_install_rpath) { | 
|  | std::string install_rpath; | 
|  | this->Target->GetInstallRPATH(this->Config, install_rpath); | 
|  | cmCLI_ExpandListUnique(install_rpath, runtimeDirs, emitted); | 
|  | } | 
|  | if (use_build_rpath) { | 
|  | // Add directories explicitly specified by user | 
|  | std::string build_rpath; | 
|  | if (this->Target->GetBuildRPATH(this->Config, build_rpath)) { | 
|  | // This will not resolve entries to use $ORIGIN, the user is expected | 
|  | // to do that if necessary. | 
|  | cmCLI_ExpandListUnique(build_rpath, runtimeDirs, emitted); | 
|  | } | 
|  | } | 
|  | if (use_build_rpath || use_link_rpath) { | 
|  | std::string rootPath; | 
|  | if (cmValue sysrootLink = | 
|  | this->Makefile->GetDefinition("CMAKE_SYSROOT_LINK")) { | 
|  | rootPath = *sysrootLink; | 
|  | } else { | 
|  | rootPath = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT"); | 
|  | } | 
|  | cmValue stagePath = this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX"); | 
|  | std::string const& installPrefix = | 
|  | this->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX"); | 
|  | cmSystemTools::ConvertToUnixSlashes(rootPath); | 
|  | std::vector<std::string> const& rdirs = this->GetRuntimeSearchPath(); | 
|  | std::string const& topBinaryDir = | 
|  | this->CMakeInstance->GetHomeOutputDirectory(); | 
|  | for (std::string const& ri : rdirs) { | 
|  | // 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) { | 
|  | std::string d = ri; | 
|  | if (!rootPath.empty() && cmHasPrefix(d, rootPath)) { | 
|  | d.erase(0, rootPath.size()); | 
|  | } else if (cmNonempty(stagePath) && cmHasPrefix(d, *stagePath)) { | 
|  | d.erase(0, (*stagePath).size()); | 
|  | d = cmStrCat(installPrefix, '/', d); | 
|  | cmSystemTools::ConvertToUnixSlashes(d); | 
|  | } else if (use_relative_build_rpath) { | 
|  | // If expansion of the $ORIGIN token is supported and permitted per | 
|  | // policy, use relative paths in the RPATH. | 
|  | if (cmSystemTools::ComparePath(d, topBinaryDir) || | 
|  | cmSystemTools::IsSubDirectory(d, topBinaryDir)) { | 
|  | d = cmSystemTools::RelativePath(targetOutputDir, d); | 
|  | if (!d.empty()) { | 
|  | d = cmStrCat(originToken, '/', d); | 
|  | } else { | 
|  | d = originToken; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (emitted.insert(d).second) { | 
|  | runtimeDirs.push_back(std::move(d)); | 
|  | } | 
|  | } else if (use_link_rpath) { | 
|  | // Do not add any path inside the source or build tree. | 
|  | std::string const& topSourceDir = | 
|  | this->CMakeInstance->GetHomeDirectory(); | 
|  | if (!cmSystemTools::ComparePath(ri, topSourceDir) && | 
|  | !cmSystemTools::ComparePath(ri, topBinaryDir) && | 
|  | !cmSystemTools::IsSubDirectory(ri, topSourceDir) && | 
|  | !cmSystemTools::IsSubDirectory(ri, topBinaryDir)) { | 
|  | std::string d = ri; | 
|  | if (!rootPath.empty() && cmHasPrefix(d, rootPath)) { | 
|  | d.erase(0, rootPath.size()); | 
|  | } else if (cmNonempty(stagePath) && cmHasPrefix(d, *stagePath)) { | 
|  | d.erase(0, (*stagePath).size()); | 
|  | d = cmStrCat(installPrefix, '/', d); | 
|  | cmSystemTools::ConvertToUnixSlashes(d); | 
|  | } | 
|  | if (emitted.insert(d).second) { | 
|  | runtimeDirs.push_back(std::move(d)); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Add runtime paths required by the languages to always be | 
|  | // present.  This is done even when skipping rpath support. | 
|  | { | 
|  | cmGeneratorTarget::LinkClosure const* lc = | 
|  | this->Target->GetLinkClosure(this->Config); | 
|  | for (std::string const& li : lc->Languages) { | 
|  | std::string useVar = cmStrCat( | 
|  | "CMAKE_", li, "_USE_IMPLICIT_LINK_DIRECTORIES_IN_RUNTIME_PATH"); | 
|  | if (this->Makefile->IsOn(useVar)) { | 
|  | std::string dirVar = | 
|  | cmStrCat("CMAKE_", li, "_IMPLICIT_LINK_DIRECTORIES"); | 
|  | if (cmValue dirs = this->Makefile->GetDefinition(dirVar)) { | 
|  | cmCLI_ExpandListUnique(*dirs, runtimeDirs, emitted); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Add runtime paths required by the platform to always be | 
|  | // present.  This is done even when skipping rpath support. | 
|  | cmCLI_ExpandListUnique(this->RuntimeAlways, runtimeDirs, emitted); | 
|  | } | 
|  |  | 
|  | std::string cmComputeLinkInformation::GetRPathString(bool for_install) const | 
|  | { | 
|  | // Get the directories to use. | 
|  | std::vector<std::string> runtimeDirs; | 
|  | this->GetRPath(runtimeDirs, for_install); | 
|  |  | 
|  | // Concatenate the paths. | 
|  | std::string rpath = cmJoin(runtimeDirs, this->GetRuntimeSep()); | 
|  |  | 
|  | // 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 reuse 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() const | 
|  | { | 
|  | if (!this->RuntimeUseChrpath) { | 
|  | return ""; | 
|  | } | 
|  |  | 
|  | return this->GetRPathString(true); | 
|  | } |