You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2645 lines
96 KiB
2645 lines
96 KiB
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt 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 "cmPolicies.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(
|
|
const cmGeneratorTarget* target, const std::string& 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) {
|
|
const char* 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);
|
|
}
|
|
|
|
// Decide whether to enable compatible library search path mode.
|
|
// There exists code that effectively does
|
|
//
|
|
// /path/to/libA.so -lB
|
|
//
|
|
// where -lB is meant to link to /path/to/libB.so. This is broken
|
|
// because it specified -lB without specifying a link directory (-L)
|
|
// in which to search for B. This worked in CMake 2.4 and below
|
|
// because -L/path/to would be added by the -L/-l split for A. In
|
|
// order to support such projects we need to add the directories
|
|
// containing libraries linked with a full path to the -L path.
|
|
this->OldLinkDirMode =
|
|
this->Target->GetPolicyStatusCMP0003() != cmPolicies::NEW;
|
|
if (this->OldLinkDirMode) {
|
|
// Construct a mask to not bother with this behavior for link
|
|
// directories already specified by the user.
|
|
this->OldLinkDirMask.insert(directories.begin(), directories.end());
|
|
}
|
|
|
|
this->CMP0060Warn = this->Makefile->PolicyOptionalWarningEnabled(
|
|
"CMAKE_POLICY_WARNING_CMP0060");
|
|
}
|
|
|
|
cmComputeLinkInformation::~cmComputeLinkInformation() = default;
|
|
|
|
namespace {
|
|
const std::string& 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>> targetLinkDirectores =
|
|
this->Target->GetLinkDirectories(this->Config, this->LinkLanguage);
|
|
|
|
const std::vector<std::string>& orderedDirectories = this->GetDirectories();
|
|
for (const std::string& dir : orderedDirectories) {
|
|
auto result =
|
|
std::find(targetLinkDirectores.begin(), targetLinkDirectores.end(), dir);
|
|
if (result != targetLinkDirectores.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;
|
|
}
|
|
|
|
const std::set<const cmGeneratorTarget*>&
|
|
cmComputeLinkInformation::GetSharedLibrariesLinked() const
|
|
{
|
|
return this->SharedLibrariesLinked;
|
|
}
|
|
|
|
const std::vector<const cmGeneratorTarget*>&
|
|
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);
|
|
cld.SetOldLinkDirMode(this->OldLinkDirMode);
|
|
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) {
|
|
const auto& 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);
|
|
}
|
|
|
|
// Finish listing compatibility paths.
|
|
if (this->OldLinkDirMode) {
|
|
// For CMake 2.4 bug-compatibility we need to consider the output
|
|
// directories of targets linked in another configuration as link
|
|
// directories.
|
|
std::set<cmGeneratorTarget const*> const& wrongItems =
|
|
cld.GetOldWrongConfigItems();
|
|
for (cmGeneratorTarget const* tgt : wrongItems) {
|
|
cmStateEnums::ArtifactType artifact = tgt->HasImportLibrary(this->Config)
|
|
? cmStateEnums::ImportLibraryArtifact
|
|
: cmStateEnums::RuntimeBinaryArtifact;
|
|
this->OldLinkDirItems.push_back(
|
|
tgt->GetFullPath(this->Config, artifact, true));
|
|
}
|
|
}
|
|
|
|
// Finish setting up linker search directories.
|
|
if (!this->FinishLinkerSearchDirectories()) {
|
|
return false;
|
|
}
|
|
|
|
// Add implicit language runtime libraries and directories.
|
|
this->AddImplicitLinkInfo();
|
|
|
|
if (!this->CMP0060WarnItems.empty()) {
|
|
std::ostringstream w;
|
|
/* clang-format off */
|
|
w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0060) << "\n"
|
|
"Some library files are in directories implicitly searched by "
|
|
"the linker when invoked for " << this->LinkLanguage << ":\n"
|
|
" " << cmJoin(this->CMP0060WarnItems, "\n ") << "\n"
|
|
"For compatibility with older versions of CMake, the generated "
|
|
"link line will ask the linker to search for these by library "
|
|
"name."
|
|
;
|
|
/* clang-format on */
|
|
this->CMakeInstance->IssueMessage(MessageType::AUTHOR_WARNING, w.str(),
|
|
this->Target->GetBacktrace());
|
|
}
|
|
|
|
// Record targets referenced by $<TARGET_OBJECTS:...> sources.
|
|
this->AddExternalObjectTargets();
|
|
|
|
return true;
|
|
}
|
|
|
|
namespace {
|
|
void FinalizeFeatureFormat(std::string& format, const std::string& activeTag,
|
|
const std::string& 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(const std::string& 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(const std::string* library,
|
|
const std::string* libItem = nullptr,
|
|
const std::string* 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;
|
|
}
|
|
|
|
const std::string* Library = nullptr;
|
|
const std::string* LibItem = nullptr;
|
|
const std::string* 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.
|
|
const std::string& 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, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmComputeLinkInformation::AddSharedDepItem(LinkEntry const& entry)
|
|
{
|
|
BT<std::string> const& item = entry.Item;
|
|
const cmGeneratorTarget* 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);
|
|
const char* 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;
|
|
const char* 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 = "(";
|
|
const char* 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;
|
|
}
|
|
|
|
// For compatibility with CMake 2.4 include the item's directory in
|
|
// the linker search path.
|
|
if (this->OldLinkDirMode && !target->IsFrameworkOnApple() &&
|
|
!cm::contains(this->OldLinkDirMask,
|
|
cmSystemTools::GetFilenamePath(item.Value))) {
|
|
this->OldLinkDirItems.push_back(item.Value);
|
|
}
|
|
|
|
const bool 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;
|
|
}
|
|
|
|
// Full path libraries should specify a valid library file name.
|
|
// See documentation of CMP0008.
|
|
std::string generator = this->GlobalGenerator->GetName();
|
|
if (this->Target->GetPolicyStatusCMP0008() != cmPolicies::NEW &&
|
|
(generator.find("Visual Studio") != std::string::npos ||
|
|
generator.find("Xcode") != std::string::npos)) {
|
|
std::string file = cmSystemTools::GetFilenameName(item.Value);
|
|
if (!this->ExtractAnyLibraryName.find(file)) {
|
|
this->HandleBadFullItem(entry, file);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// This is called to handle a link item that is a full path.
|
|
// If the target is not a static library make sure the link type is
|
|
// shared. This is because dynamic-mode linking can handle both
|
|
// shared and static libraries but static-mode can handle only
|
|
// static libraries. If a previous user item changed the link type
|
|
// to static we need to make sure it is back to shared.
|
|
if (this->LinkTypeEnabled) {
|
|
std::string name = cmSystemTools::GetFilenameName(item.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);
|
|
}
|
|
}
|
|
|
|
// For compatibility with CMake 2.4 include the item's directory in
|
|
// the linker search path.
|
|
if (this->OldLinkDirMode &&
|
|
!cm::contains(this->OldLinkDirMask,
|
|
cmSystemTools::GetFilenamePath(item.Value))) {
|
|
this->OldLinkDirItems.push_back(item.Value);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Check the policy for whether we should use the approach below.
|
|
switch (this->Target->GetPolicyStatusCMP0060()) {
|
|
case cmPolicies::WARN:
|
|
if (this->CMP0060Warn) {
|
|
// Print the warning at most once for this item.
|
|
std::string const& wid =
|
|
cmStrCat("CMP0060-WARNING-GIVEN-", item.Value);
|
|
if (!this->CMakeInstance->GetPropertyAsBool(wid)) {
|
|
this->CMakeInstance->SetProperty(wid, "1");
|
|
this->CMP0060WarnItems.insert(item.Value);
|
|
}
|
|
}
|
|
CM_FALLTHROUGH;
|
|
case cmPolicies::OLD:
|
|
break;
|
|
case cmPolicies::REQUIRED_ALWAYS:
|
|
case cmPolicies::REQUIRED_IF_USED:
|
|
case cmPolicies::NEW:
|
|
return false;
|
|
}
|
|
|
|
// Many system linkers support multiple architectures by
|
|
// automatically selecting the implicit linker search path for the
|
|
// current architecture. If the library appears in an implicit link
|
|
// directory then just report the file name without the directory
|
|
// portion. This will allow the system linker to locate the proper
|
|
// library for the architecture at link time.
|
|
LinkEntry fileEntry{ entry };
|
|
fileEntry.Item = file;
|
|
this->AddUserItem(fileEntry, false);
|
|
|
|
// Make sure the link directory ordering will find the library.
|
|
this->OrderLinkerSearchPath->AddLinkLibrary(item.Value);
|
|
|
|
return true;
|
|
}
|
|
|
|
void cmComputeLinkInformation::AddUserItem(LinkEntry const& entry,
|
|
bool pathNotKnown)
|
|
{
|
|
// This is called to handle a link item that does not match a CMake
|
|
// target and is not a full path. We check here if it looks like a
|
|
// library file name to automatically request the proper link type
|
|
// from the linker. For example:
|
|
//
|
|
// foo ==> -lfoo
|
|
// libfoo.a ==> -Wl,-Bstatic -lfoo
|
|
|
|
BT<std::string> const& item = entry.Item;
|
|
|
|
if (item.Value[0] == '-' || item.Value[0] == '$' || item.Value[0] == '`') {
|
|
// Pass flags through untouched.
|
|
// if this is a -l option then we might need to warn about
|
|
// CMP0003 so put it in OldUserFlagItems, if it is not a -l
|
|
// or -Wl,-l (-framework -pthread), then allow it without a
|
|
// CMP0003 as -L will not affect those other linker flags
|
|
if (cmHasLiteralPrefix(item.Value, "-l") ||
|
|
cmHasLiteralPrefix(item.Value, "-Wl,-l")) {
|
|
// This is a linker option provided by the user.
|
|
this->OldUserFlagItems.push_back(item.Value);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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 {
|
|
// This is a name specified by the user.
|
|
if (pathNotKnown) {
|
|
this->OldUserFlagItems.push_back(item.Value);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
const std::string& 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, false);
|
|
|
|
// Make sure the link directory ordering will find the library.
|
|
this->OrderLinkerSearchPath->AddLinkLibrary(entry.Item.Value);
|
|
}
|
|
|
|
void cmComputeLinkInformation::HandleBadFullItem(LinkEntry const& entry,
|
|
std::string const& file)
|
|
{
|
|
std::string const& item = entry.Item.Value;
|
|
// Do not depend on things that do not exist.
|
|
auto i = std::find(this->Depends.begin(), this->Depends.end(), item);
|
|
if (i != this->Depends.end()) {
|
|
this->Depends.erase(i);
|
|
}
|
|
|
|
// Tell the linker to search for the item and provide the proper
|
|
// path for it. Do not contribute to any CMP0003 warning (do not
|
|
// put in OldLinkDirItems or OldUserFlagItems).
|
|
LinkEntry fileEntry{ entry };
|
|
fileEntry.Item = file;
|
|
this->AddUserItem(fileEntry, false);
|
|
this->OrderLinkerSearchPath->AddLinkLibrary(item);
|
|
|
|
// Produce any needed message.
|
|
switch (this->Target->GetPolicyStatusCMP0008()) {
|
|
case cmPolicies::WARN: {
|
|
// Print the warning at most once for this item.
|
|
std::string wid = cmStrCat("CMP0008-WARNING-GIVEN-", item);
|
|
if (!this->CMakeInstance->GetState()->GetGlobalPropertyAsBool(wid)) {
|
|
this->CMakeInstance->GetState()->SetGlobalProperty(wid, "1");
|
|
std::ostringstream w;
|
|
/* clang-format off */
|
|
w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0008) << "\n"
|
|
"Target \"" << this->Target->GetName() << "\" links to item\n"
|
|
" " << item << "\n"
|
|
"which is a full-path but not a valid library file name.";
|
|
/* clang-format on */
|
|
this->CMakeInstance->IssueMessage(MessageType::AUTHOR_WARNING, w.str(),
|
|
this->Target->GetBacktrace());
|
|
}
|
|
}
|
|
CM_FALLTHROUGH;
|
|
case cmPolicies::OLD: // NOLINT(bugprone-branch-clone)
|
|
// OLD behavior does not warn.
|
|
break;
|
|
case cmPolicies::NEW:
|
|
// NEW behavior will not get here.
|
|
break;
|
|
case cmPolicies::REQUIRED_IF_USED:
|
|
case cmPolicies::REQUIRED_ALWAYS: {
|
|
std::ostringstream e;
|
|
/* clang-format off */
|
|
e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0008) << "\n"
|
|
"Target \"" << this->Target->GetName() << "\" links to item\n"
|
|
" " << item << "\n"
|
|
"which is a full-path but not a valid library file name.";
|
|
/* clang-format on */
|
|
this->CMakeInstance->IssueMessage(MessageType::FATAL_ERROR, e.str(),
|
|
this->Target->GetBacktrace());
|
|
} break;
|
|
}
|
|
}
|
|
|
|
bool cmComputeLinkInformation::FinishLinkerSearchDirectories()
|
|
{
|
|
// Support broken projects if necessary.
|
|
if (this->OldLinkDirItems.empty() || this->OldUserFlagItems.empty() ||
|
|
!this->OldLinkDirMode) {
|
|
return true;
|
|
}
|
|
|
|
// Enforce policy constraints.
|
|
switch (this->Target->GetPolicyStatusCMP0003()) {
|
|
case cmPolicies::WARN:
|
|
if (!this->CMakeInstance->GetState()->GetGlobalPropertyAsBool(
|
|
"CMP0003-WARNING-GIVEN")) {
|
|
this->CMakeInstance->GetState()->SetGlobalProperty(
|
|
"CMP0003-WARNING-GIVEN", "1");
|
|
std::ostringstream w;
|
|
this->PrintLinkPolicyDiagnosis(w);
|
|
this->CMakeInstance->IssueMessage(MessageType::AUTHOR_WARNING, w.str(),
|
|
this->Target->GetBacktrace());
|
|
}
|
|
CM_FALLTHROUGH;
|
|
case cmPolicies::OLD:
|
|
// OLD behavior is to add the paths containing libraries with
|
|
// known full paths as link directories.
|
|
break;
|
|
case cmPolicies::NEW:
|
|
// Should never happen due to assignment of OldLinkDirMode
|
|
return true;
|
|
case cmPolicies::REQUIRED_IF_USED:
|
|
case cmPolicies::REQUIRED_ALWAYS: {
|
|
std::ostringstream e;
|
|
e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0003) << '\n';
|
|
this->PrintLinkPolicyDiagnosis(e);
|
|
this->CMakeInstance->IssueMessage(MessageType::FATAL_ERROR, e.str(),
|
|
this->Target->GetBacktrace());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Add the link directories for full path items.
|
|
for (std::string const& i : this->OldLinkDirItems) {
|
|
this->OrderLinkerSearchPath->AddLinkLibrary(i);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void cmComputeLinkInformation::PrintLinkPolicyDiagnosis(std::ostream& os)
|
|
{
|
|
// Tell the user what to do.
|
|
/* clang-format off */
|
|
os << "Policy CMP0003 should be set before this line. "
|
|
"Add code such as\n"
|
|
" if(COMMAND cmake_policy)\n"
|
|
" cmake_policy(SET CMP0003 NEW)\n"
|
|
" endif(COMMAND cmake_policy)\n"
|
|
"as early as possible but after the most recent call to "
|
|
"cmake_minimum_required or cmake_policy(VERSION). ";
|
|
/* clang-format on */
|
|
|
|
// List the items that might need the old-style paths.
|
|
os << "This warning appears because target \"" << this->Target->GetName()
|
|
<< "\" links to some libraries for which the linker must search:\n";
|
|
{
|
|
// Format the list of unknown items to be as short as possible while
|
|
// still fitting in the allowed width (a true solution would be the
|
|
// bin packing problem if we were allowed to change the order).
|
|
std::string::size_type max_size = 76;
|
|
std::string line;
|
|
const char* sep = " ";
|
|
for (std::string const& i : this->OldUserFlagItems) {
|
|
// If the addition of another item will exceed the limit then
|
|
// output the current line and reset it. Note that the separator
|
|
// is either " " or ", " which is always 2 characters.
|
|
if (!line.empty() && (line.size() + i.size() + 2) > max_size) {
|
|
os << line << '\n';
|
|
sep = " ";
|
|
line.clear();
|
|
}
|
|
line += sep;
|
|
line += i;
|
|
// Convert to the other separator.
|
|
sep = ", ";
|
|
}
|
|
if (!line.empty()) {
|
|
os << line << '\n';
|
|
}
|
|
}
|
|
|
|
// List the paths old behavior is adding.
|
|
os << "and other libraries with known full path:\n";
|
|
std::set<std::string> emitted;
|
|
for (std::string const& i : this->OldLinkDirItems) {
|
|
if (emitted.insert(cmSystemTools::GetFilenamePath(i)).second) {
|
|
os << " " << i << '\n';
|
|
}
|
|
}
|
|
|
|
// Explain.
|
|
os << "CMake is adding directories in the second list to the linker "
|
|
"search path in case they are needed to find libraries from the "
|
|
"first list (for backwards compatibility with CMake 2.4). "
|
|
"Set policy CMP0003 to OLD or NEW to enable or disable this "
|
|
"behavior explicitly. "
|
|
"Run \"cmake --help-policy CMP0003\" for more information.";
|
|
}
|
|
|
|
void cmComputeLinkInformation::LoadImplicitLinkInfo()
|
|
{
|
|
// 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);
|
|
const char* 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);
|
|
}
|