Genex: Add $<LINK_LIBRARY:...>

This generator expression offers the capability, for the link step, to
decorate libraries with prefix/suffix flags and/or adding any specific flag for each
library.

Fixes: #22812, #18751, #20078, #22703
This commit is contained in:
Marc Chevrier
2022-02-08 00:02:32 +01:00
parent 78dd7d5292
commit 42965799b4
131 changed files with 1823 additions and 149 deletions
@@ -1113,6 +1113,75 @@ Output-Related Expressions
property, perhaps via the :command:`target_link_libraries` command,
to specify private link dependencies without other usage requirements.
.. genex:: $<LINK_LIBRARY:feature,library-list>
.. versionadded:: 3.24
Manage how libraries are specified during the link step.
This expression may be used to specify how to link libraries in a target.
For example:
.. code-block:: cmake
add_library(lib1 STATIC ...)
add_library(lib2 ...)
target_link_libraries(lib2 PRIVATE $<LINK_LIBRARY:whole_archive,lib1>)
This specify to use the ``lib1`` target with feature ``whole_archive`` for
linking target ``lib2``. The feature must have be defined by
:variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>` variable or, if
:variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>_SUPPORTED` is false,
by :variable:`CMAKE_LINK_USING_<FEATURE>` variable.
.. note::
The evaluation of this generator expression will use, for the following
variables, the values defined at the level of the creation of the target:
* :variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>_SUPPORTED`
* :variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>`
* :variable:`CMAKE_LINK_USING_<FEATURE>_SUPPORTED`
* :variable:`CMAKE_LINK_USING_<FEATURE>`
This expression can only be used to specify link libraries (i.e. part of
:command:`link_libraries` or :command:`target_link_libraries` commands and
:prop_tgt:`LINK_LIBRARIES` or :prop_tgt:`INTERFACE_LINK_LIBRARIES` target
properties).
.. note::
If this expression appears in the :prop_tgt:`INTERFACE_LINK_LIBRARIES`
property of a target, it will be included in the imported target generated
by :command:`install(EXPORT)` command. It is the responsibility of the
environment consuming this import to define the link feature used by this
expression.
The ``library-list`` argument can hold CMake targets or external libraries.
Any ``CMake`` target of type :ref:`OBJECT <Object Libraries>` or
:ref:`INTERFACE <Interface Libraries>` will be ignored by this expression and
will be handled in the standard way.
Each target or external library involved in the link step must have only one
kind of feature (the absence of feature is also incompatible with any
feature). For example:
.. code-block:: cmake
add_library(lib1 ...)
add_library(lib2 ...)
target_link_libraries(lib2 PUBLIC $<LINK_LIBRARY:feature1,lib1>)
add_library(lib3 ...)
target_link_libraries(lib3 PRIVATE lib1 lib2)
# an error will be raised here because lib1 has two different features
.. note::
This expression does not guarantee that the list of specified libraries
will be kept grouped. So, constructs like ``start-group`` and
``end-group``, as supported by ``GNU ld``, cannot be used.
.. genex:: $<INSTALL_INTERFACE:...>
Content of ``...`` when the property is exported using :command:`install(EXPORT)`,
+4
View File
@@ -440,6 +440,8 @@ Variables that Control the Build
/variable/CMAKE_LANG_LINKER_LAUNCHER
/variable/CMAKE_LANG_LINK_LIBRARY_FILE_FLAG
/variable/CMAKE_LANG_LINK_LIBRARY_FLAG
/variable/CMAKE_LANG_LINK_USING_FEATURE
/variable/CMAKE_LANG_LINK_USING_FEATURE_SUPPORTED
/variable/CMAKE_LANG_LINK_WHAT_YOU_USE_FLAG
/variable/CMAKE_LANG_VISIBILITY_PRESET
/variable/CMAKE_LIBRARY_OUTPUT_DIRECTORY
@@ -450,6 +452,8 @@ Variables that Control the Build
/variable/CMAKE_LINK_INTERFACE_LIBRARIES
/variable/CMAKE_LINK_LIBRARY_FILE_FLAG
/variable/CMAKE_LINK_LIBRARY_FLAG
/variable/CMAKE_LINK_USING_FEATURE
/variable/CMAKE_LINK_USING_FEATURE_SUPPORTED
/variable/CMAKE_LINK_WHAT_YOU_USE
/variable/CMAKE_LINK_WHAT_YOU_USE_CHECK
/variable/CMAKE_MACOSX_BUNDLE
+8
View File
@@ -0,0 +1,8 @@
Genex-LINK_LIBRARY
------------------
* The :genex:`LINK_LIBRARY` generator expression was added to manage how
libraries are specified during the link step. The variables
:variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>` and
:variable:`CMAKE_LINK_USING_<FEATURE>` are used to define features usable by
the :genex:`LINK_LIBRARY` generator expression.
@@ -0,0 +1,19 @@
CMAKE_<LANG>_LINK_USING_<FEATURE>
---------------------------------
.. versionadded:: 3.24
This variable defines, for the specified ``<FEATURE>`` and the linker language
``<LANG>``, the expression expected by the linker when libraries are specified
using :genex:`LINK_LIBRARY` generator expression.
.. note::
Feature names defined in all uppercase are reserved to CMake.
See also the associated variable
:variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>_SUPPORTED` and
:variable:`CMAKE_LINK_USING_<FEATURE>` variable for the definition of features
independent from the link language.
.. include:: CMAKE_LINK_USING_FEATURE.txt
@@ -0,0 +1,13 @@
CMAKE_<LANG>_LINK_USING_<FEATURE>_SUPPORTED
-------------------------------------------
.. versionadded:: 3.24
Set to ``TRUE`` if the ``<FEATURE>``, as defined by variable
:variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>`, is supported for the linker
language ``<LANG>``.
.. note::
This variable is evaluated before the more generic variable
:variable:`CMAKE_LINK_USING_<FEATURE>_SUPPORTED`.
@@ -0,0 +1,23 @@
CMAKE_LINK_USING_<FEATURE>
--------------------------
.. versionadded:: 3.24
This variable defines, for the specified ``FEATURE``, the expression expected
by the linker, regardless the linker language, when libraries are specified
using :genex:`LINK_LIBRARY` generator expression.
.. note::
Feature names defined in all uppercase are reserved to CMake.
See also the associated variable
:variable:`CMAKE_LINK_USING_<FEATURE>_SUPPORTED` and
:variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>` variable for the definition of
features dependent from the link language.
This variable will be used by :genex:`LINK_LIBRARY` generator expression if,
for the linker language, the variable
:variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>_SUPPORTED` is false or not set.
.. include:: CMAKE_LINK_USING_FEATURE.txt
+111
View File
@@ -0,0 +1,111 @@
It can contain one or three elements.
::
[<PREFIX>] <LIBRARY_EXPRESSION> [<SUFFIX>]
When ``<PREFIX>`` and/or ``<SUFFIX>`` are specified, they encapsulate the list
of libraries.
.. note::
Even if ``<PREFIX>`` and ``<SUFFIX>`` are specified, there is not guarantee
that the list of specified libraries, as part of :genex:`LINK_LIBRARY`
generator expression, will be kept grouped. So, constructs like
``start-group`` and ``end-group``, as supported by ``GNU ld``, cannot be
used.
``<LIBRARY_EXPRESSION>`` is used to specify the decoration for each
library. For that purpose, the patterns ``<LIBRARY>``, ``<LINK_ITEM>``, and
``<LIB_ITEM>`` are available:
* ``<LIBRARY>`` is expanded to the library as computed by CMake.
* ``<LINK_ITEM>`` is expanded to the same expression as if the library was
specified in the standard way.
* ``<LIB_ITEM>`` is equivalent to ``<LIBRARY>`` for CMake targets and is
expanded to the item specified by the user for external libraries.
Moreover, it is possible to have different decorations for paths (CMake targets
and external libraries specified with absolute paths) and other items specified
by name. For that purpose, ``PATH{}`` and ``NAME{}`` wrappers can be used.
For all three elements of this variable, the ``LINKER:`` prefix can be used:
.. include:: ../command/LINK_OPTIONS_LINKER.txt
:start-line: 3
Examples
^^^^^^^^
Loading a whole static library
""""""""""""""""""""""""""""""
A common need is the capability to load a whole static library. This capability
is offered by various environments but with a specific syntax:
.. code-block:: cmake
set(CMAKE_C_LINK_USING_whole_archive_SUPPORTED TRUE)
if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
set(CMAKE_C_LINK_USING_whole_archive "-force_load <LIB_ITEM>")
elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU"
AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(CMAKE_C_LINK_USING_whole_archive "LINKER:--push-state,--whole-archive"
"<LINK_ITEM>"
"LINKER:--pop-state")
elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
set(CMAKE_C_LINK_USING_whole_archive "/WHOLEARCHIVE:<LIBRARY>")
else()
# feature not yet supported for the other environments
set(CMAKE_C_LINK_USING_whole_archive_SUPPORTED FALSE)
endif()
add_library(lib1 STATIC ...)
add_library(lib2 SHARED ...)
if(CMAKE_C_LINK_USING_whole_archive_SUPPORTED)
target_link_libraries(lib2 PRIVATE
$<LINK_LIBRARY:whole_archive,lib1,$<IF:$<LINK_LANG_AND_ID:C,Clang>,libexternal.a,external>>)
else()
target_link_libraries(lib2 PRIVATE lib1 external)
endif()
CMake will generate the following link expressions:
* ``Clang``: ``-force_load /path/to/lib1.a -force_load libexternal.a``
* ``GNU``: ``-Wl,--whole-archive /path/to/lib1.a -lexternal -Wl,--no-whole-archive``
* ``MSVC``: ``/WHOLEARCHIVE:/path/to/lib1.lib /WHOLEARCHIVE:external.lib``
CMake will ensure, when possible, that ``<PREFIX>`` and ``<SUFFIX>`` are
not repeated for each library.
In case of ``Clang``, the pattern ``<LIB_ITEM>`` is used because we need to
specify the library as defined by the user, not the name computed by CMake
(in that case ``external``).
Linking a library as weak
"""""""""""""""""""""""""
On MacOS, it is possible to link a library in weak mode (the library and all
references are marked as weak imports), but different flags must be used for a
library specified by path and by name. This constraint by be solved by using
``PATH{}`` and ``NAME{}`` wrappers:
.. code-block:: cmake
if (CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
set(CMAKE_LINK_USING_weak_library "PATH{-weak_library <LIBRARY>}NAME{LINKER:-weak-l<LIB_ITEM>}")
set(CMAKE_LINK_USING_weak_library_SUPPORTED TRUE)
endif()
add_library(lib SHARED ...)
add_executable(main ...)
if(CMAKE_LINK_USING_weak_library_SUPPORTED)
target_link_libraries(main PRIVATE $<LINK_LIBRARY:weak_library,lib,external>)
else()
target_link_libraries(main PRIVATE lib external)
endif()
CMake will generate the following link expression:
``-weak_library /path/to/lib -Xlinker -weak-lexternal``
@@ -0,0 +1,14 @@
CMAKE_LINK_USING_<FEATURE>_SUPPORTED
------------------------------------
.. versionadded:: 3.24
Set to ``TRUE`` if the ``<FEATURE>``, as defined by variable
:variable:`CMAKE_LINK_USING_<FEATURE>`, is supported regardless the linker
language.
.. note::
This variable is evaluated if, and only if, the variable
:variable:`CMAKE_<LANG>_LINK_USING_<FEATURE>_SUPPORTED` evaluates to
``FALSE``.
+143 -34
View File
@@ -11,6 +11,7 @@
#include <utility>
#include <cm/memory>
#include <cmext/string_view>
#include "cmComputeComponentGraph.h"
#include "cmGeneratorTarget.h"
@@ -18,6 +19,7 @@
#include "cmListFileCache.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmRange.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
@@ -174,8 +176,33 @@ items that we know the linker will re-use automatically (shared libs).
*/
namespace {
const auto LL_BEGIN = "<LINK_LIBRARY:"_s;
const auto LL_END = "</LINK_LIBRARY:"_s;
inline std::string ExtractFeature(std::string const& item)
{
return item.substr(LL_BEGIN.length(),
item.find('>', LL_BEGIN.length()) - LL_BEGIN.length());
}
bool IsFeatureSupported(cmMakefile* makefile, std::string const& linkLanguage,
std::string const& feature)
{
auto featureSupported =
cmStrCat("CMAKE_", linkLanguage, "_LINK_USING_", feature, "_SUPPORTED");
if (makefile->GetDefinition(featureSupported).IsOn()) {
return true;
}
featureSupported = cmStrCat("CMAKE_LINK_USING_", feature, "_SUPPORTED");
return makefile->GetDefinition(featureSupported).IsOn();
}
}
cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
const std::string& config)
const std::string& config,
const std::string& linkLanguage)
{
// Store context information.
this->Target = target;
@@ -183,6 +210,7 @@ cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
this->GlobalGenerator =
this->Target->GetLocalGenerator()->GetGlobalGenerator();
this->CMakeInstance = this->GlobalGenerator->GetCMakeInstance();
this->LinkLanguage = linkLanguage;
// The configuration being linked.
this->HasConfig = !config.empty();
@@ -249,7 +277,7 @@ cmComputeLinkDepends::Compute()
}
// Compute the final ordering.
this->OrderLinkEntires();
this->OrderLinkEntries();
// Compute the final set of link entries.
// Iterate in reverse order so we can keep only the last occurrence
@@ -281,32 +309,33 @@ cmComputeLinkDepends::Compute()
return this->FinalLinkEntries;
}
std::map<cmLinkItem, int>::iterator cmComputeLinkDepends::AllocateLinkEntry(
cmLinkItem const& item)
std::pair<std::map<cmLinkItem, int>::iterator, bool>
cmComputeLinkDepends::AllocateLinkEntry(cmLinkItem const& item)
{
std::map<cmLinkItem, int>::value_type index_entry(
item, static_cast<int>(this->EntryList.size()));
auto lei = this->LinkEntryIndex.insert(index_entry).first;
this->EntryList.emplace_back();
this->InferredDependSets.emplace_back();
this->EntryConstraintGraph.emplace_back();
auto lei = this->LinkEntryIndex.insert(index_entry);
if (lei.second) {
this->EntryList.emplace_back();
this->InferredDependSets.emplace_back();
this->EntryConstraintGraph.emplace_back();
}
return lei;
}
int cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item)
std::pair<int, bool> cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item)
{
// Allocate a spot for the item entry.
auto lei = this->AllocateLinkEntry(item);
// Check if the item entry has already been added.
auto lei = this->LinkEntryIndex.find(item);
if (lei != this->LinkEntryIndex.end()) {
if (!lei.second) {
// Yes. We do not need to follow the item's dependencies again.
return lei->second;
return { lei.first->second, false };
}
// Allocate a spot for the item entry.
lei = this->AllocateLinkEntry(item);
// Initialize the item entry.
int index = lei->second;
int index = lei.first->second;
LinkEntry& entry = this->EntryList[index];
entry.Item = BT<std::string>(item.AsStr(), item.Backtrace);
entry.Target = item.Target;
@@ -332,22 +361,21 @@ int cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item)
}
}
return index;
return { index, true };
}
void cmComputeLinkDepends::AddLinkObject(cmLinkItem const& item)
{
// Allocate a spot for the item entry.
auto lei = this->AllocateLinkEntry(item);
// Check if the item entry has already been added.
auto lei = this->LinkEntryIndex.find(item);
if (lei != this->LinkEntryIndex.end()) {
if (!lei.second) {
return;
}
// Allocate a spot for the item entry.
lei = this->AllocateLinkEntry(item);
// Initialize the item entry.
int index = lei->second;
int index = lei.first->second;
LinkEntry& entry = this->EntryList[index];
entry.Item = BT<std::string>(item.AsStr(), item.Backtrace);
entry.IsObject = true;
@@ -423,14 +451,14 @@ void cmComputeLinkDepends::QueueSharedDependencies(
void cmComputeLinkDepends::HandleSharedDependency(SharedDepEntry const& dep)
{
// Check if the target already has an entry.
auto lei = this->LinkEntryIndex.find(dep.Item);
if (lei == this->LinkEntryIndex.end()) {
// Allocate a spot for the item entry.
lei = this->AllocateLinkEntry(dep.Item);
// Allocate a spot for the item entry.
auto lei = this->AllocateLinkEntry(dep.Item);
int index = lei.first->second;
// Check if the target does not already has an entry.
if (lei.second) {
// Initialize the item entry.
LinkEntry& entry = this->EntryList[lei->second];
LinkEntry& entry = this->EntryList[index];
entry.Item = BT<std::string>(dep.Item.AsStr(), dep.Item.Backtrace);
entry.Target = dep.Item.Target;
@@ -441,7 +469,6 @@ void cmComputeLinkDepends::HandleSharedDependency(SharedDepEntry const& dep)
}
// Get the link entry for this target.
int index = lei->second;
LinkEntry& entry = this->EntryList[index];
// This shared library dependency must follow the item that listed
@@ -541,6 +568,7 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index,
{
// Track inferred dependency sets implied by this list.
std::map<int, DependSet> dependSets;
std::string feature;
// Loop over the libraries linked directly by the depender.
for (T const& l : libs) {
@@ -550,9 +578,86 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index,
if (item.AsStr() == this->Target->GetName() || item.AsStr().empty()) {
continue;
}
if (cmHasPrefix(item.AsStr(), LL_BEGIN) &&
cmHasSuffix(item.AsStr(), '>')) {
feature = ExtractFeature(item.AsStr());
// emit a warning if an undefined feature is used as part of
// an imported target
if (depender_index >= 0) {
const auto& depender = this->EntryList[depender_index];
if (depender.Target != nullptr && depender.Target->IsImported() &&
!IsFeatureSupported(this->Makefile, this->LinkLanguage, feature)) {
this->CMakeInstance->IssueMessage(
MessageType::AUTHOR_ERROR,
cmStrCat("The 'IMPORTED' target '", depender.Target->GetName(),
"' uses the generator-expression '$<LINK_LIBRARY>' with "
"the feature '",
feature,
"', which is undefined or unsupported.\nDid you miss to "
"define it by setting variables \"CMAKE_",
this->LinkLanguage, "_LINK_USING_", feature,
"\" and \"CMAKE_", this->LinkLanguage, "_LINK_USING_",
feature, "_SUPPORTED\"?"),
this->Target->GetBacktrace());
}
}
continue;
}
if (cmHasPrefix(item.AsStr(), LL_END) && cmHasSuffix(item.AsStr(), '>')) {
feature.clear();
continue;
}
// Add a link entry for this item.
int dependee_index = this->AddLinkEntry(l);
auto ale = this->AddLinkEntry(item);
int dependee_index = ale.first;
LinkEntry& entry = this->EntryList[dependee_index];
if (!feature.empty()) {
if (ale.second) {
// current item not yet defined
if (entry.Target != nullptr &&
(entry.Target->GetType() ==
cmStateEnums::TargetType::OBJECT_LIBRARY ||
entry.Target->GetType() ==
cmStateEnums::TargetType::INTERFACE_LIBRARY)) {
this->CMakeInstance->IssueMessage(
MessageType::AUTHOR_WARNING,
cmStrCat("The feature '", feature,
"', specified as part of a generator-expression "
"'$",
LL_BEGIN, feature, ">', will not be applied to the ",
(entry.Target->GetType() ==
cmStateEnums::TargetType::OBJECT_LIBRARY
? "OBJECT"
: "INTERFACE"),
" library '", entry.Item.Value, "'."),
this->Target->GetBacktrace());
} else {
entry.Feature = feature;
}
}
}
bool supportedItem = entry.Target == nullptr ||
(entry.Target->GetType() != cmStateEnums::TargetType::OBJECT_LIBRARY &&
entry.Target->GetType() != cmStateEnums::TargetType::INTERFACE_LIBRARY);
if (supportedItem && entry.Feature != feature) {
// incompatibles features occurred
this->CMakeInstance->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat(
"Impossible to link target '", this->Target->GetName(),
"' because the link item '", entry.Item.Value, "', specified ",
(feature.empty() ? "without any feature"
: cmStrCat("with the feature '", feature, '\'')),
", has already occurred ",
(entry.Feature.empty()
? "without any feature"
: cmStrCat("with the feature '", entry.Feature, '\'')),
", which is not allowed."),
this->Target->GetBacktrace());
}
// The dependee must come after the depender.
if (depender_index >= 0) {
@@ -667,7 +772,7 @@ void cmComputeLinkDepends::DisplayConstraintGraph()
fprintf(stderr, "%s\n", e.str().c_str());
}
void cmComputeLinkDepends::OrderLinkEntires()
void cmComputeLinkDepends::OrderLinkEntries()
{
// Compute the DAG of strongly connected components. The algorithm
// used by cmComputeComponentGraph should identify the components in
@@ -869,10 +974,14 @@ void cmComputeLinkDepends::DisplayFinalEntries()
fprintf(stderr, "target [%s] links to:\n", this->Target->GetName().c_str());
for (LinkEntry const& lei : this->FinalLinkEntries) {
if (lei.Target) {
fprintf(stderr, " target [%s]\n", lei.Target->GetName().c_str());
fprintf(stderr, " target [%s]", lei.Target->GetName().c_str());
} else {
fprintf(stderr, " item [%s]\n", lei.Item.Value.c_str());
fprintf(stderr, " item [%s]", lei.Item.Value.c_str());
}
if (!lei.Feature.empty()) {
fprintf(stderr, ", feature [%s]", lei.Feature.c_str());
}
fprintf(stderr, "\n");
}
fprintf(stderr, "\n");
}
+9 -4
View File
@@ -30,7 +30,8 @@ class cmComputeLinkDepends
{
public:
cmComputeLinkDepends(cmGeneratorTarget const* target,
const std::string& config);
const std::string& config,
const std::string& linkLanguage);
~cmComputeLinkDepends();
cmComputeLinkDepends(const cmComputeLinkDepends&) = delete;
@@ -51,6 +52,9 @@ public:
bool IsSharedDep = false;
bool IsFlag = false;
bool IsObject = false;
// The following member is for the management of items specified
// through genex $<LINK_LIBRARY:...>
std::string Feature;
};
using EntryVector = std::vector<LinkEntry>;
@@ -68,12 +72,13 @@ private:
cmMakefile* Makefile;
cmGlobalGenerator const* GlobalGenerator;
cmake* CMakeInstance;
std::string LinkLanguage;
std::string Config;
EntryVector FinalLinkEntries;
std::map<cmLinkItem, int>::iterator AllocateLinkEntry(
std::pair<std::map<cmLinkItem, int>::iterator, bool> AllocateLinkEntry(
cmLinkItem const& item);
int AddLinkEntry(cmLinkItem const& item);
std::pair<int, bool> AddLinkEntry(cmLinkItem const& item);
void AddLinkObject(cmLinkItem const& item);
void AddVarLinkEntries(int depender_index, const char* value);
void AddDirectLinkEntries();
@@ -131,7 +136,7 @@ private:
void DisplayConstraintGraph();
// Ordering algorithm.
void OrderLinkEntires();
void OrderLinkEntries();
std::vector<char> ComponentVisited;
std::vector<int> ComponentOrder;
+362 -25
View File
@@ -19,6 +19,7 @@
#include "cmMessageType.h"
#include "cmOrderDirectories.h"
#include "cmOutputConverter.h"
#include "cmPlaceholderExpander.h"
#include "cmPolicies.h"
#include "cmState.h"
#include "cmStateTypes.h"
@@ -344,6 +345,27 @@ cmComputeLinkInformation::cmComputeLinkInformation(
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",
FeatureDescriptor{ "__CMAKE_LINK_LIBRARY",
cmStrCat(this->GetLibLinkFileFlag(), "<LIBRARY>") });
}
if (!this->GetObjLinkFileFlag().empty()) {
this->LibraryFeatureDescriptors.emplace(
"__CMAKE_LINK_OBJECT",
FeatureDescriptor{ "__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",
FeatureDescriptor{ "__CMAKE_LINK_EXECUTABLE",
cmStrCat(this->LoaderFlag, "<LIBRARY>") });
}
// Check the platform policy for missing soname case.
this->NoSONameUsesPath =
this->Makefile->IsOn("CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME");
@@ -510,12 +532,40 @@ bool cmComputeLinkInformation::Compute()
}
// Compute the ordered link line items.
cmComputeLinkDepends cld(this->Target, this->Config);
cmComputeLinkDepends cld(this->Target, this->Config, this->LinkLanguage);
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 (currentFeature != nullptr &&
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.empty() &&
(currentFeature == nullptr ||
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.IsSharedDep) {
this->AddSharedDepItem(linkEntry);
} else {
@@ -523,6 +573,16 @@ bool cmComputeLinkInformation::Compute()
}
}
if (currentFeature != nullptr) {
// 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");
@@ -575,6 +635,270 @@ bool cmComputeLinkInformation::Compute()
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;
}
}
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_USING_", feature);
cmValue featureSupported =
this->Makefile->GetDefinition(cmStrCat(featureName, "_SUPPORTED"));
if (!featureSupported.IsOn()) {
featureName = cmStrCat("CMAKE_LINK_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(), true);
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, FeatureDescriptor{ feature, items[0].Value, items[1].Value });
} else {
this->LibraryFeatureDescriptors.emplace(
feature,
FeatureDescriptor{ 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;
}
namespace {
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 != nullptr && variable == "LIBRARY") {
return *this->Library;
}
if (this->LibItem != nullptr && variable == "LIB_ITEM") {
return *this->LibItem;
}
if (this->LinkItem != nullptr && 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))
{
}
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);
}
void cmComputeLinkInformation::AddImplicitLinkInfo()
{
// The link closure lists all languages whose implicit info is needed.
@@ -657,23 +981,21 @@ void cmComputeLinkInformation::AddItem(LinkEntry const& entry)
if (impexe && this->LoaderFlag) {
// This link item is an executable that may provide symbols
// used by this target. A special flag is needed on this
// platform. Add it now.
std::string linkItem = this->LoaderFlag;
// 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);
linkItem += exe;
this->Items.emplace_back(BT<std::string>(linkItem, item.Backtrace),
ItemIsPath::Yes, ItemIsObject::No, tgt);
this->Items.emplace_back(
BT<std::string>(exe, item.Backtrace), ItemIsPath::Yes, tgt,
this->FindLibraryFeature(
entry.Feature.empty() ? "__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, ItemIsObject::No,
tgt);
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);
@@ -1098,7 +1420,10 @@ void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry)
}
// Now add the full path to the library.
this->Items.emplace_back(item, ItemIsPath::Yes, ItemIsObject::No, target);
this->Items.emplace_back(item, ItemIsPath::Yes, target,
this->FindLibraryFeature(entry.Feature.empty()
? "__CMAKE_LINK_LIBRARY"
: entry.Feature));
}
void cmComputeLinkInformation::AddFullItem(LinkEntry const& entry)
@@ -1154,9 +1479,12 @@ void cmComputeLinkInformation::AddFullItem(LinkEntry const& entry)
}
// Now add the full path to the library.
this->Items.emplace_back(item, ItemIsPath::Yes,
entry.IsObject ? ItemIsObject::Yes
: ItemIsObject::No);
this->Items.emplace_back(
item, ItemIsPath::Yes, nullptr,
this->FindLibraryFeature(
entry.Feature.empty()
? (entry.IsObject ? "__CMAKE_LINK_OBJECT" : "__CMAKE_LINK_LIBRARY")
: entry.Feature));
}
bool cmComputeLinkInformation::CheckImplicitDirItem(LinkEntry const& entry)
@@ -1221,8 +1549,6 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(LinkEntry const& entry)
return true;
}
// void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item,
// bool pathNotKnown)
void cmComputeLinkInformation::AddUserItem(LinkEntry const& entry,
bool pathNotKnown)
{
@@ -1236,8 +1562,8 @@ void cmComputeLinkInformation::AddUserItem(LinkEntry const& entry,
BT<std::string> const& item = entry.Item;
// Pass flags through untouched.
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
@@ -1322,9 +1648,20 @@ void cmComputeLinkInformation::AddUserItem(LinkEntry const& entry,
}
// Create an option to ask the linker to search for the library.
std::string out = cmStrCat(this->LibLinkFlag, lib, this->LibLinkSuffix);
this->Items.emplace_back(BT<std::string>(out, item.Backtrace),
ItemIsPath::No);
auto out = cmStrCat(this->LibLinkFlag, lib, this->LibLinkSuffix);
if (!entry.Feature.empty()) {
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
@@ -1374,10 +1711,10 @@ void cmComputeLinkInformation::DropDirectoryItem(BT<std::string> const& item)
// 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."),
cmStrCat("Target \"", this->Target->GetName(),
"\" requests linking to directory \"", item.Value,
"\". Targets may link only to libraries. CMake is dropping "
"the item."),
item.Backtrace);
}
@@ -1804,8 +2141,8 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs,
// 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.
// This will not resolve entries to use $ORIGIN, the user is expected
// to do that if necessary.
cmCLI_ExpandListUnique(build_rpath, runtimeDirs, emitted);
}
}
+53 -12
View File
@@ -5,6 +5,7 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include <iosfwd>
#include <map>
#include <memory>
#include <set>
#include <string>
@@ -28,6 +29,9 @@ class cmake;
*/
class cmComputeLinkInformation
{
private:
class FeatureDescriptor;
public:
cmComputeLinkInformation(cmGeneratorTarget const* target,
const std::string& config);
@@ -43,28 +47,33 @@ public:
Yes,
};
enum class ItemIsObject
{
No,
Yes,
};
struct Item
{
Item() = default;
Item(BT<std::string> v, ItemIsPath isPath,
ItemIsObject isObject = ItemIsObject::No,
cmGeneratorTarget const* target = nullptr)
cmGeneratorTarget const* target = nullptr,
FeatureDescriptor const* feature = nullptr)
: Value(std::move(v))
, IsPath(isPath)
, IsObject(isObject)
, Target(target)
, Feature(feature)
{
}
BT<std::string> Value;
ItemIsPath IsPath = ItemIsPath::Yes;
ItemIsObject IsObject = ItemIsObject::No;
ItemIsPath IsPath = ItemIsPath::No;
cmGeneratorTarget const* Target = nullptr;
bool HasFeature() const { return this->Feature != nullptr; }
BT<std::string> GetFormattedItem(std::string const& path) const
{
return { (this->Feature != nullptr)
? this->Feature->GetDecoratedItem(path, this->IsPath)
: path,
Value.Backtrace };
}
private:
FeatureDescriptor const* Feature = nullptr;
};
using ItemVector = std::vector<Item>;
void AppendValues(std::string& result, std::vector<BT<std::string>>& values);
@@ -237,4 +246,36 @@ private:
void AddLibraryRuntimeInfo(std::string const& fullPath,
const cmGeneratorTarget* target);
void AddLibraryRuntimeInfo(std::string const& fullPath);
class FeatureDescriptor
{
public:
FeatureDescriptor() = default;
FeatureDescriptor(std::string name, std::string itemFormat);
FeatureDescriptor(std::string name, std::string itemPathFormat,
std::string itemNameFormat);
FeatureDescriptor(std::string name, std::string prefix,
std::string itemPathFormat, std::string itemNameFormat,
std::string suffix);
const std::string Name;
const bool Supported = false;
const std::string Prefix;
const std::string Suffix;
std::string GetDecoratedItem(std::string const& library,
ItemIsPath isPath) const;
std::string GetDecoratedItem(std::string const& library,
std::string const& linkItem,
std::string const& defaultValue,
ItemIsPath isPath) const;
private:
std::string ItemPathFormat;
std::string ItemNameFormat;
};
std::map<std::string, FeatureDescriptor> LibraryFeatureDescriptors;
bool AddLibraryFeature(std::string const& feature);
FeatureDescriptor const& GetLibraryFeature(std::string const& feature) const;
FeatureDescriptor const* FindLibraryFeature(
std::string const& feature) const;
};
+63
View File
@@ -1198,6 +1198,68 @@ static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode
}
} linkLanguageAndIdNode;
static const struct LinkLibraryNode : public cmGeneratorExpressionNode
{
LinkLibraryNode() {} // NOLINT(modernize-use-equals-default)
int NumExpectedParameters() const override { return OneOrMoreParameters; }
std::string Evaluate(
const std::vector<std::string>& parameters,
cmGeneratorExpressionContext* context,
const GeneratorExpressionContent* content,
cmGeneratorExpressionDAGChecker* dagChecker) const override
{
if (!context->HeadTarget || !dagChecker ||
!dagChecker->EvaluatingLinkLibraries()) {
reportError(context, content->GetOriginalExpression(),
"$<LINK_LIBRARY:...> may only be used with binary targets "
"to specify link libraries.");
return std::string();
}
std::vector<std::string> list;
cmExpandLists(parameters.begin(), parameters.end(), list);
if (list.empty()) {
reportError(
context, content->GetOriginalExpression(),
"$<LINK_LIBRARY:...> expects a feature name as first argument.");
return std::string();
}
if (list.size() == 1) {
// no libraries specified, ignore this genex
return std::string();
}
auto const& feature = list.front();
const auto LL_BEGIN = cmStrCat("<LINK_LIBRARY:", feature, '>');
const auto LL_END = cmStrCat("</LINK_LIBRARY:", feature, '>');
// filter out $<LINK_LIBRARY:..> tags with same feature
// and raise an error for any different feature
cm::erase_if(list, [&](const std::string& item) -> bool {
return item == LL_BEGIN || item == LL_END;
});
auto it =
std::find_if(list.cbegin() + 1, list.cend(),
[&feature](const std::string& item) -> bool {
return cmHasPrefix(item, "<LINK_LIBRARY:"_s) &&
item.substr(14, item.find('>', 14) - 14) != feature;
});
if (it != list.cend()) {
reportError(
context, content->GetOriginalExpression(),
"$<LINK_LIBRARY:...> with different features cannot be nested.");
return std::string();
}
list.front() = LL_BEGIN;
list.push_back(LL_END);
return cmJoin(list, ";"_s);
}
} linkLibraryNode;
static const struct HostLinkNode : public cmGeneratorExpressionNode
{
HostLinkNode() {} // NOLINT(modernize-use-equals-default)
@@ -2668,6 +2730,7 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
{ "COMPILE_LANGUAGE", &languageNode },
{ "LINK_LANG_AND_ID", &linkLanguageAndIdNode },
{ "LINK_LANGUAGE", &linkLanguageNode },
{ "LINK_LIBRARY", &linkLibraryNode },
{ "HOST_LINK", &hostLinkNode },
{ "DEVICE_LINK", &deviceLinkNode },
{ "SHELL_PATH", &shellPathNode }
+12 -3
View File
@@ -4625,7 +4625,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions(
}
std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper(
std::vector<BT<std::string>>& result, const std::string& language) const
std::vector<BT<std::string>>& result, const std::string& language,
bool joinItems) const
{
// replace "LINKER:" prefixed elements by actual linker wrapper
const std::string wrapper(this->Makefile->GetSafeDefinition(
@@ -4684,7 +4685,14 @@ std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper(
std::vector<BT<std::string>> options = wrapOptions(
linkerOptions, bt, wrapperFlag, wrapperSep, concatFlagAndArgs);
result.insert(entry, options.begin(), options.end());
if (joinItems) {
result.insert(entry,
cmJoin(cmRange<decltype(options.cbegin())>(
options.cbegin(), options.cend()),
" "_s));
} else {
result.insert(entry, options.begin(), options.end());
}
}
return result;
}
@@ -6377,7 +6385,8 @@ bool cmGeneratorTarget::VerifyLinkItemIsTarget(LinkItemRole role,
std::string const& str = item.AsStr();
if (!str.empty() &&
(str[0] == '-' || str[0] == '$' || str[0] == '`' ||
str.find_first_of("/\\") != std::string::npos)) {
str.find_first_of("/\\") != std::string::npos ||
cmHasPrefix(str, "<LINK_LIBRARY:"_s))) {
return true;
}
+2 -1
View File
@@ -513,7 +513,8 @@ public:
std::string const& config, std::string const& language) const;
std::vector<BT<std::string>>& ResolveLinkerWrapper(
std::vector<BT<std::string>>& result, const std::string& language) const;
std::vector<BT<std::string>>& result, const std::string& language,
bool joinItems = false) const;
void GetStaticLibraryLinkOptions(std::vector<std::string>& result,
const std::string& config,
+3 -1
View File
@@ -3741,7 +3741,9 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
}
libPaths.Add("-framework " + this->XCodeEscapePath(fwName));
} else {
libPaths.Add(this->XCodeEscapePath(cleanPath));
libPaths.Add(
libName.GetFormattedItem(this->XCodeEscapePath(cleanPath))
.Value);
}
if ((!libName.Target || libName.Target->IsImported()) &&
IsLinkPhaseLibraryExtension(libPath)) {
+2
View File
@@ -35,5 +35,7 @@ bool cmLinkLibrariesCommand(std::vector<std::string> const& args,
mf.AppendProperty("LINK_LIBRARIES", *i);
}
mf.CheckProperty("LINK_LIBRARIES");
return true;
}
+2 -8
View File
@@ -75,14 +75,8 @@ void cmLinkLineComputer::ComputeLinkLibs(
BT<std::string> linkLib;
if (item.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) {
if (item.IsObject == cmComputeLinkInformation::ItemIsObject::Yes) {
linkLib.Value += cli.GetObjLinkFileFlag();
} else {
linkLib.Value += cli.GetLibLinkFileFlag();
}
linkLib.Value += this->ConvertToOutputFormat(
this->ConvertToLinkReference(item.Value.Value));
linkLib.Backtrace = item.Value.Backtrace;
linkLib = item.GetFormattedItem(this->ConvertToOutputFormat(
this->ConvertToLinkReference(item.Value.Value)));
} else {
linkLib = item.Value;
}
+5 -3
View File
@@ -118,8 +118,10 @@ void cmLinkLineDeviceComputer::ComputeLinkLibraries(
// can tolerate '.so' or '.dylib' it cannot tolerate '.so.1'.
if (cmHasLiteralSuffix(item.Value.Value, ".a") ||
cmHasLiteralSuffix(item.Value.Value, ".lib")) {
linkLib.Value += this->ConvertToOutputFormat(
this->ConvertToLinkReference(item.Value.Value));
linkLib.Value = item
.GetFormattedItem(this->ConvertToOutputFormat(
this->ConvertToLinkReference(item.Value.Value)))
.Value;
}
} else if (item.Value == "-framework") {
// This is the first part of '-framework Name' where the framework
@@ -127,7 +129,7 @@ void cmLinkLineDeviceComputer::ComputeLinkLibraries(
skipItemAfterFramework = true;
continue;
} else if (cmLinkItemValidForDevice(item.Value.Value)) {
linkLib.Value += item.Value.Value;
linkLib.Value = item.Value.Value;
}
if (emitted.insert(linkLib.Value).second) {
+3 -1
View File
@@ -1294,7 +1294,9 @@ void cmLocalVisualStudio7GeneratorInternals::OutputLibraries(
for (auto const& lib : libs) {
if (lib.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) {
std::string rel = lg->MaybeRelativeToCurBinDir(lib.Value.Value);
fout << lg->ConvertToXMLOutputPath(rel) << " ";
rel = lg->ConvertToXMLOutputPath(rel);
fout << (lib.HasFeature() ? lib.GetFormattedItem(rel).Value : rel)
<< " ";
} else if (!lib.Target ||
lib.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
fout << lib.Value.Value << " ";
+25
View File
@@ -3976,6 +3976,31 @@ std::vector<std::string> cmMakefile::GetPropertyKeys() const
return this->StateSnapshot.GetDirectory().GetPropertyKeys();
}
void cmMakefile::CheckProperty(const std::string& prop) const
{
// Certain properties need checking.
if (prop == "LINK_LIBRARIES") {
if (cmValue value = this->GetProperty(prop)) {
// Look for <LINK_LIBRARY:> internal pattern
static cmsys::RegularExpression linkLibrary(
"(^|;)(</?LINK_LIBRARY:[^;>]*>)(;|$)");
if (!linkLibrary.find(value)) {
return;
}
// Report an error.
this->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat(
"Property ", prop, " contains the invalid item \"",
linkLibrary.match(2), "\". The ", prop,
" property may contain the generator-expression "
"\"$<LINK_LIBRARY:...>\" "
"which may be used to specify how the libraries are linked."));
}
}
}
cmTarget* cmMakefile::FindLocalNonAliasTarget(const std::string& name) const
{
auto i = this->Targets.find(name);
+1
View File
@@ -787,6 +787,7 @@ public:
cmValue GetProperty(const std::string& prop, bool chain) const;
bool GetPropertyAsBool(const std::string& prop) const;
std::vector<std::string> GetPropertyKeys() const;
void CheckProperty(const std::string& prop) const;
//! Initialize a makefile from its parent
void InitializeFromParent(cmMakefile* parent);
+81 -55
View File
@@ -1779,69 +1779,93 @@ void cmTarget::InsertPrecompileHeader(BT<std::string> const& entry)
this->impl->PrecompileHeadersEntries.push_back(entry);
}
static void cmTargetCheckLINK_INTERFACE_LIBRARIES(const std::string& prop,
const std::string& value,
cmMakefile* context,
bool imported)
namespace {
void CheckLinkLibraryPattern(const std::string& property,
const std::string& value, cmMakefile* context)
{
// Look for link-type keywords in the value.
static cmsys::RegularExpression keys("(^|;)(debug|optimized|general)(;|$)");
if (!keys.find(value)) {
// Look for <LINK_LIBRARY:> and </LINK_LIBRARY:> internal tags
static cmsys::RegularExpression linkLibrary(
"(^|;)(</?LINK_LIBRARY:[^;>]*>)(;|$)");
if (!linkLibrary.find(value)) {
return;
}
// Report an error.
context->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Property ", property, " contains the invalid item \"",
linkLibrary.match(2), "\". The ", property,
" property may contain the generator-expression "
"\"$<LINK_LIBRARY:...>\" "
"which may be used to specify how the libraries are linked."));
}
void CheckLINK_INTERFACE_LIBRARIES(const std::string& prop,
const std::string& value,
cmMakefile* context, bool imported)
{
// Support imported and non-imported versions of the property.
const char* base = (imported ? "IMPORTED_LINK_INTERFACE_LIBRARIES"
: "LINK_INTERFACE_LIBRARIES");
// Report an error.
std::ostringstream e;
e << "Property " << prop << " may not contain link-type keyword \""
<< keys.match(2) << "\". "
<< "The " << base << " property has a per-configuration "
<< "version called " << base << "_<CONFIG> which may be "
<< "used to specify per-configuration rules.";
if (!imported) {
e << " "
<< "Alternatively, an IMPORTED library may be created, configured "
<< "with a per-configuration location, and then named in the "
<< "property value. "
<< "See the add_library command's IMPORTED mode for details."
<< "\n"
<< "If you have a list of libraries that already contains the "
<< "keyword, use the target_link_libraries command with its "
<< "LINK_INTERFACE_LIBRARIES mode to set the property. "
<< "The command automatically recognizes link-type keywords and sets "
<< "the LINK_INTERFACE_LIBRARIES and LINK_INTERFACE_LIBRARIES_DEBUG "
<< "properties accordingly.";
// Look for link-type keywords in the value.
static cmsys::RegularExpression keys("(^|;)(debug|optimized|general)(;|$)");
if (keys.find(value)) {
// Report an error.
std::ostringstream e;
e << "Property " << prop << " may not contain link-type keyword \""
<< keys.match(2) << "\". "
<< "The " << base << " property has a per-configuration "
<< "version called " << base << "_<CONFIG> which may be "
<< "used to specify per-configuration rules.";
if (!imported) {
e << " "
<< "Alternatively, an IMPORTED library may be created, configured "
<< "with a per-configuration location, and then named in the "
<< "property value. "
<< "See the add_library command's IMPORTED mode for details."
<< "\n"
<< "If you have a list of libraries that already contains the "
<< "keyword, use the target_link_libraries command with its "
<< "LINK_INTERFACE_LIBRARIES mode to set the property. "
<< "The command automatically recognizes link-type keywords and sets "
<< "the LINK_INTERFACE_LIBRARIES and LINK_INTERFACE_LIBRARIES_DEBUG "
<< "properties accordingly.";
}
context->IssueMessage(MessageType::FATAL_ERROR, e.str());
}
context->IssueMessage(MessageType::FATAL_ERROR, e.str());
CheckLinkLibraryPattern(base, value, context);
}
static void cmTargetCheckINTERFACE_LINK_LIBRARIES(const std::string& value,
cmMakefile* context)
void CheckLINK_LIBRARIES(const std::string& value, cmMakefile* context)
{
CheckLinkLibraryPattern("LINK_LIBRARIES", value, context);
}
void CheckINTERFACE_LINK_LIBRARIES(const std::string& value,
cmMakefile* context)
{
// Look for link-type keywords in the value.
static cmsys::RegularExpression keys("(^|;)(debug|optimized|general)(;|$)");
if (!keys.find(value)) {
return;
if (keys.find(value)) {
// Report an error.
std::ostringstream e;
e << "Property INTERFACE_LINK_LIBRARIES may not contain link-type "
"keyword \""
<< keys.match(2)
<< "\". The INTERFACE_LINK_LIBRARIES "
"property may contain configuration-sensitive generator-expressions "
"which may be used to specify per-configuration rules.";
context->IssueMessage(MessageType::FATAL_ERROR, e.str());
}
// Report an error.
std::ostringstream e;
e << "Property INTERFACE_LINK_LIBRARIES may not contain link-type "
"keyword \""
<< keys.match(2)
<< "\". The INTERFACE_LINK_LIBRARIES "
"property may contain configuration-sensitive generator-expressions "
"which may be used to specify per-configuration rules.";
context->IssueMessage(MessageType::FATAL_ERROR, e.str());
CheckLinkLibraryPattern("INTERFACE_LINK_LIBRARIES", value, context);
}
static void cmTargetCheckIMPORTED_GLOBAL(const cmTarget* target,
cmMakefile* context)
void CheckIMPORTED_GLOBAL(const cmTarget* target, cmMakefile* context)
{
const auto& targets = context->GetOwnedImportedTargets();
auto it =
@@ -1857,6 +1881,7 @@ static void cmTargetCheckIMPORTED_GLOBAL(const cmTarget* target,
context->IssueMessage(MessageType::FATAL_ERROR, e.str());
}
}
}
void cmTarget::CheckProperty(const std::string& prop,
cmMakefile* context) const
@@ -1864,22 +1889,23 @@ void cmTarget::CheckProperty(const std::string& prop,
// Certain properties need checking.
if (cmHasLiteralPrefix(prop, "LINK_INTERFACE_LIBRARIES")) {
if (cmValue value = this->GetProperty(prop)) {
cmTargetCheckLINK_INTERFACE_LIBRARIES(prop, *value, context, false);
CheckLINK_INTERFACE_LIBRARIES(prop, *value, context, false);
}
}
if (cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES")) {
} else if (cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES")) {
if (cmValue value = this->GetProperty(prop)) {
cmTargetCheckLINK_INTERFACE_LIBRARIES(prop, *value, context, true);
CheckLINK_INTERFACE_LIBRARIES(prop, *value, context, true);
}
}
if (prop == "INTERFACE_LINK_LIBRARIES") {
} else if (prop == "LINK_LIBRARIES") {
if (cmValue value = this->GetProperty(prop)) {
cmTargetCheckINTERFACE_LINK_LIBRARIES(*value, context);
CheckLINK_LIBRARIES(*value, context);
}
}
if (prop == "IMPORTED_GLOBAL") {
} else if (prop == "INTERFACE_LINK_LIBRARIES") {
if (cmValue value = this->GetProperty(prop)) {
CheckINTERFACE_LINK_LIBRARIES(*value, context);
}
} else if (prop == "IMPORTED_GLOBAL") {
if (this->IsImported()) {
cmTargetCheckIMPORTED_GLOBAL(this, context);
CheckIMPORTED_GLOBAL(this, context);
}
}
}
+3
View File
@@ -318,6 +318,9 @@ bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args,
target->SetProperty("LINK_INTERFACE_LIBRARIES", "");
}
target->CheckProperty("LINK_LIBRARIES", &mf);
target->CheckProperty("INTERFACE_LINK_LIBRARIES", &mf);
return true;
}
+4 -2
View File
@@ -3677,7 +3677,8 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions(
this->LocalGenerator->MaybeRelativeToCurBinDir(l.Value.Value);
ConvertToWindowsSlash(path);
if (!cmVS10IsTargetsFile(l.Value.Value)) {
libVec.push_back(path);
libVec.push_back(l.HasFeature() ? l.GetFormattedItem(path).Value
: path);
}
} else {
libVec.push_back(l.Value.Value);
@@ -4315,7 +4316,8 @@ void cmVisualStudio10TargetGenerator::AddLibraries(
if (cmVS10IsTargetsFile(l.Value.Value)) {
vsTargetVec.push_back(path);
} else {
libVec.push_back(path);
libVec.push_back(l.HasFeature() ? l.GetFormattedItem(path).Value
: path);
}
} else if (!l.Target ||
l.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
+12
View File
@@ -312,6 +312,7 @@ add_RunCMake_test(GenEx-LINK_LANGUAGE)
add_RunCMake_test(GenEx-LINK_LANG_AND_ID)
add_RunCMake_test(GenEx-HOST_LINK)
add_RunCMake_test(GenEx-DEVICE_LINK)
add_RunCMake_test(GenEx-LINK_LIBRARY)
add_RunCMake_test(GenEx-TARGET_FILE -DLINKER_SUPPORTS_PDB=${LINKER_SUPPORTS_PDB})
add_RunCMake_test(GenEx-GENEX_EVAL)
add_RunCMake_test(GenEx-TARGET_RUNTIME_DLLS)
@@ -652,6 +653,17 @@ add_RunCMake_test(target_link_libraries)
add_RunCMake_test(target_link_libraries-ALIAS)
add_RunCMake_test(target_link_libraries-LINK_LANGUAGE)
add_RunCMake_test(target_link_libraries-LINK_LANG_AND_ID)
add_RunCMake_test(target_link_libraries-LINK_LIBRARY -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
-DMINGW=${MINGW}
-DMSYS=${MSYS}
-DCYGWIN=${CYGWIN}
-DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
-DMSVC_VERSION=${MSVC_VERSION}
-DCMAKE_SHARED_LIBRARY_PREFIX=${CMAKE_SHARED_LIBRARY_PREFIX}
-DCMAKE_SHARED_LIBRARY_SUFFIX=${CMAKE_SHARED_LIBRARY_SUFFIX}
-DCMAKE_IMPORT_LIBRARY_PREFIX=${CMAKE_IMPORT_LIBRARY_PREFIX}
-DCMAKE_IMPORT_LIBRARY_SUFFIX=${CMAKE_IMPORT_LIBRARY_SUFFIX}
-DCMAKE_LINK_LIBRARY_FLAG=${CMAKE_LINK_LIBRARY_FLAG})
add_RunCMake_test(add_link_options -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID})
add_RunCMake_test(target_link_options -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
-DCMake_TEST_CUDA=${CMake_TEST_CUDA})
@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.18...3.22)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
@@ -0,0 +1,25 @@
include(RunCMake)
run_cmake(add_custom_target)
run_cmake(add_custom_command)
run_cmake(add_link_options)
run_cmake(link_directories)
run_cmake(target_link_options)
run_cmake(target_link_directories)
run_cmake(no-arguments)
run_cmake(empty-arguments)
run_cmake(forbidden-arguments)
run_cmake(bad-feature1)
run_cmake(bad-feature2)
run_cmake(bad-feature3)
run_cmake(bad-feature4)
run_cmake(bad-feature5)
run_cmake(bad-feature6)
run_cmake(bad-feature7)
run_cmake(feature-not-supported)
run_cmake(library-ignored)
run_cmake(compatible-features)
run_cmake(incompatible-features)
run_cmake(nested-compatible-features)
run_cmake(nested-incompatible-features)
run_cmake(only-targets)
@@ -0,0 +1 @@
1
@@ -0,0 +1,9 @@
CMake Error at add_custom_command.cmake:[0-9]+ \(add_custom_command\):
Error evaluating generator expression:
\$<LINK_LIBRARY:feat>
\$<LINK_LIBRARY:...> may only be used with binary targets to specify link
libraries.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,4 @@
add_custom_target(drive)
add_custom_command(TARGET drive PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "$<LINK_LIBRARY:feat>"
)
@@ -0,0 +1 @@
1
@@ -0,0 +1,9 @@
CMake Error at add_custom_target.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<LINK_LIBRARY:feat>
\$<LINK_LIBRARY:...> may only be used with binary targets to specify link
libraries.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,3 @@
add_custom_target(drive
COMMAND ${CMAKE_COMMAND} -E echo "$<LINK_LIBRARY:feat>"
)
@@ -0,0 +1 @@
1
@@ -0,0 +1,9 @@
CMake Error at add_link_options.cmake:[0-9]+ \(add_link_options\):
Error evaluating generator expression:
\$<LINK_LIBRARY:feat>
\$<LINK_LIBRARY:...> may only be used with binary targets to specify link
libraries.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,5 @@
enable_language(C)
add_link_options("$<LINK_LIBRARY:feat>")
add_library(empty SHARED empty.c)
@@ -0,0 +1 @@
1
@@ -0,0 +1,6 @@
CMake Error at bad-feature1.cmake:[0-9]+ \(add_library\):
Feature 'bad_feat', specified through generator-expression
'\$<LINK_LIBRARY>' to link target 'lib', is not supported for the 'C' link
language.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,6 @@
enable_language(C)
add_library(dep SHARED empty.c)
add_library(lib SHARED empty.c)
target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:bad_feat,dep>")
@@ -0,0 +1 @@
1
@@ -0,0 +1,5 @@
CMake Error at bad-feature2.cmake:[0-9]+ \(add_library\):
Feature 'feat', specified through generator-expression '\$<LINK_LIBRARY>' to
link target 'lib', is not defined for the 'C' link language.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,8 @@
enable_language(C)
set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
add_library(dep SHARED empty.c)
add_library(lib SHARED empty.c)
target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
@@ -0,0 +1 @@
1
@@ -0,0 +1,6 @@
CMake Error at bad-feature3.cmake:[0-9]+ \(add_library\):
Feature 'feat', specified by variable 'CMAKE_C_LINK_USING_feat', is
malformed \("<LIBRARY>", "<LIB_ITEM>", or "<LINK_ITEM>" patterns are
missing\) and cannot be used to link target 'lib'.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,9 @@
enable_language(C)
set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
set(CMAKE_C_LINK_USING_feat "")
add_library(dep SHARED empty.c)
add_library(lib SHARED empty.c)
target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
@@ -0,0 +1 @@
1
@@ -0,0 +1,6 @@
CMake Error at bad-feature4.cmake:[0-9]+ \(add_library\):
Feature 'feat', specified by variable 'CMAKE_C_LINK_USING_feat', is
malformed \("<LIBRARY>", "<LIB_ITEM>", or "<LINK_ITEM>" patterns are
missing\) and cannot be used to link target 'lib'.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,9 @@
enable_language(C)
set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
set(CMAKE_C_LINK_USING_feat "-opt")
add_library(dep SHARED empty.c)
add_library(lib SHARED empty.c)
target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
@@ -0,0 +1 @@
1
@@ -0,0 +1,6 @@
CMake Error at bad-feature5.cmake:[0-9]+ \(add_library\):
Feature 'feat', specified by variable 'CMAKE_C_LINK_USING_feat', is
malformed \(wrong number of elements\) and cannot be used to link target
'lib'.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,9 @@
enable_language(C)
set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
set(CMAKE_C_LINK_USING_feat "-prefix" "<LIBRARY>")
add_library(dep SHARED empty.c)
add_library(lib SHARED empty.c)
target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
@@ -0,0 +1 @@
1
@@ -0,0 +1,6 @@
CMake Error at bad-feature6.cmake:[0-9]+ \(add_library\):
Feature 'feat', specified by variable 'CMAKE_C_LINK_USING_feat', is
malformed \("<LIBRARY>", "<LIB_ITEM>", or "<LINK_ITEM>" patterns are missing
for "PATH{}" alternative\) and cannot be used to link target 'lib'.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,9 @@
enable_language(C)
set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
set(CMAKE_C_LINK_USING_feat "PATH{}NAME{<LIBRARY>}")
add_library(dep SHARED empty.c)
add_library(lib SHARED empty.c)
target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
@@ -0,0 +1 @@
1
@@ -0,0 +1,6 @@
CMake Error at bad-feature7.cmake:[0-9]+ \(add_library\):
Feature 'feat', specified by variable 'CMAKE_C_LINK_USING_feat', is
malformed \("<LIBRARY>", "<LIB_ITEM>", or "<LINK_ITEM>" patterns are missing
for "NAME{}" alternative\) and cannot be used to link target 'lib'.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,9 @@
enable_language(C)
set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
set(CMAKE_C_LINK_USING_feat "NAME{}PATH{<LIBRARY>}")
add_library(dep SHARED empty.c)
add_library(lib SHARED empty.c)
target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
@@ -0,0 +1,15 @@
enable_language(C)
set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
set(CMAKE_C_LINK_USING_feat1 "<LIBRARY>")
set(CMAKE_C_LINK_USING_feat2_SUPPORTED TRUE)
set(CMAKE_C_LINK_USING_feat2 "<LIBRARY>")
add_library(dep1 SHARED empty.c)
add_library(dep2 SHARED empty.c)
target_link_libraries(dep2 PRIVATE "$<LINK_LIBRARY:feat1,dep1>")
add_library(lib SHARED empty.c)
target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat2,dep1,dep2>")
@@ -0,0 +1 @@
1
@@ -0,0 +1,8 @@
CMake Error at empty-arguments.cmake:[0-9]+ \(target_link_libraries\):
Error evaluating generator expression:
\$<LINK_LIBRARY:,>
\$<LINK_LIBRARY:...> expects a feature name as first argument.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,4 @@
enable_language(C)
add_library(lib SHARED empty.c)
target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:,>")
@@ -0,0 +1,5 @@
CMake Error at feature-not-supported.cmake:[0-9]+ \(add_library\):
Feature 'feat', specified through generator-expression '\$<LINK_LIBRARY>' to
link target 'lib', is not supported for the 'C' link language.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,9 @@
enable_language(C)
set(CMAKE_C_LINK_USING_feat_SUPPORTED FALSE)
set(CMAKE_C_LINK_USING_feat "<LIBRARY>")
add_library(dep SHARED empty.c)
add_library(lib SHARED empty.c)
target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat,dep>")
@@ -0,0 +1 @@
1
@@ -0,0 +1,16 @@
CMake Error at forbidden-arguments.cmake:[0-9]+ \(link_libraries\):
Property LINK_LIBRARIES contains the invalid item "<LINK_LIBRARY:feat>".
The LINK_LIBRARIES property may contain the generator-expression
"\$<LINK_LIBRARY:...>" which may be used to specify how the libraries are
linked.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
CMake Error at forbidden-arguments.cmake:[0-9]+ \(target_link_libraries\):
Property LINK_LIBRARIES contains the invalid item "<LINK_LIBRARY:feat>".
The LINK_LIBRARIES property may contain the generator-expression
"\$<LINK_LIBRARY:...>" which may be used to specify how the libraries are
linked.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,6 @@
enable_language(C)
link_libraries(<LINK_LIBRARY:feat> foo </LINK_LIBRARY:feat>)
add_library(lib SHARED empty.c)
target_link_libraries(lib PRIVATE <LINK_LIBRARY:feat> foo </LINK_LIBRARY:feat>)
@@ -0,0 +1,6 @@
CMake Error at incompatible-features.cmake:[0-9]+ \(add_library\):
Impossible to link target 'lib' because the link item 'dep1', specified
with the feature 'feat1', has already occurred with the feature 'feat2',
which is not allowed.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,15 @@
enable_language(C)
set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
set(CMAKE_C_LINK_USING_feat1 "<LIBRARY>")
set(CMAKE_C_LINK_USING_feat2_SUPPORTED TRUE)
set(CMAKE_C_LINK_USING_feat2 "<LIBRARY>")
add_library(dep1 SHARED empty.c)
add_library(dep2 SHARED empty.c)
target_link_libraries(dep2 PUBLIC "$<LINK_LIBRARY:feat1,dep1>")
add_library(lib SHARED empty.c)
target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat2,dep1,dep2>")
@@ -0,0 +1,14 @@
CMake Warning \(dev\) at library-ignored.cmake:[0-9]+ \(add_library\):
The feature 'feat', specified as part of a generator-expression
'\$<LINK_LIBRARY:feat>', will not be applied to the INTERFACE library
'front'.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
This warning is for project developers. Use -Wno-dev to suppress it.
CMake Warning \(dev\) at library-ignored.cmake:[0-9]+ \(add_library\):
The feature 'feat', specified as part of a generator-expression
'\$<LINK_LIBRARY:feat>', will not be applied to the OBJECT library 'dep'.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
This warning is for project developers. Use -Wno-dev to suppress it.
@@ -0,0 +1,15 @@
enable_language(C)
set(CMAKE_C_LINK_USING_feat_SUPPORTED TRUE)
set(CMAKE_C_LINK_USING_feat "<LIBRARY>")
add_library(dep OBJECT empty.c)
add_library(lib SHARED empty.c)
add_library(front INTERFACE)
target_link_libraries(front INTERFACE lib)
add_library(lib2 SHARED empty.c)
target_link_libraries(lib2 PRIVATE "$<LINK_LIBRARY:feat,front,dep>")
@@ -0,0 +1 @@
1
@@ -0,0 +1,9 @@
CMake Error at link_directories.cmake:[0-9]+ \(link_directories\):
Error evaluating generator expression:
\$<LINK_LIBRARY:feat>
\$<LINK_LIBRARY:...> may only be used with binary targets to specify link
libraries.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,5 @@
enable_language(C)
link_directories("$<LINK_LIBRARY:feat>")
add_library(empty SHARED empty.c)
@@ -0,0 +1,11 @@
enable_language(C)
set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
set(CMAKE_C_LINK_USING_feat1 "<LIBRARY>")
add_library(dep1 SHARED empty.c)
add_library(dep2 SHARED empty.c)
add_library(lib SHARED empty.c)
target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat1,dep1,$<LINK_LIBRARY:feat1,dep2>>")
@@ -0,0 +1,8 @@
CMake Error at nested-incompatible-features.cmake:[0-9]+ \(target_link_libraries\):
Error evaluating generator expression:
\$<LINK_LIBRARY:feat2,dep1,\$<LINK_LIBRARY:feat1,dep2>>
\$<LINK_LIBRARY:...> with different features cannot be nested.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,14 @@
enable_language(C)
set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
set(CMAKE_C_LINK_USING_feat1 "<LIBRARY>")
set(CMAKE_C_LINK_USING_feat2_SUPPORTED TRUE)
set(CMAKE_C_LINK_USING_feat2 "<LIBRARY>")
add_library(dep1 SHARED empty.c)
add_library(dep2 SHARED empty.c)
add_library(lib SHARED empty.c)
target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feat2,dep1,$<LINK_LIBRARY:feat1,dep2>>")
@@ -0,0 +1 @@
1
@@ -0,0 +1,8 @@
CMake Error at no-arguments.cmake:[0-9]+ \(target_link_libraries\):
Error evaluating generator expression:
\$<LINK_LIBRARY>
\$<LINK_LIBRARY> expression requires at least one parameter.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,4 @@
enable_language(C)
add_library(lib SHARED empty.c)
target_link_libraries(lib PRIVATE "$<LINK_LIBRARY>")
@@ -0,0 +1 @@
1
@@ -0,0 +1,13 @@
CMake Error at only-targets.cmake:[0-9]+ \(target_link_libraries\):
Target "lib2" has LINK_LIBRARIES_ONLY_TARGETS enabled, but it links to:
external
which is not a target. Possible reasons include:
\* There is a typo in the target name.
\* A find_package call is missing for an IMPORTED target.
\* An ALIAS target is missing.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,16 @@
enable_language(C)
set(CMAKE_C_LINK_USING_feat1_SUPPORTED TRUE)
set(CMAKE_C_LINK_USING_feat1 "<LIBRARY>")
set(CMAKE_LINK_LIBRARIES_ONLY_TARGETS 1)
add_library(dep1 SHARED empty.c)
add_library(lib1 SHARED empty.c)
# accepted
target_link_libraries(lib1 PRIVATE "$<LINK_LIBRARY:feat1,dep1>")
add_library(lib2 SHARED empty.c)
# invalid
target_link_libraries(lib2 PRIVATE "$<LINK_LIBRARY:feat1,external>")
@@ -0,0 +1,9 @@
CMake Error at target_link_directories.cmake:[0-9]+ \(target_link_directories\):
Error evaluating generator expression:
\$<LINK_LIBRARY:feat>
\$<LINK_LIBRARY:...> may only be used with binary targets to specify link
libraries.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,4 @@
enable_language(C)
add_library(empty SHARED empty.c)
target_link_directories(empty PRIVATE "$<LINK_LIBRARY:feat>")
@@ -0,0 +1 @@
1
@@ -0,0 +1,9 @@
CMake Error at target_link_options.cmake:[0-9]+ \(target_link_options\):
Error evaluating generator expression:
\$<LINK_LIBRARY:FEAT>
\$<LINK_LIBRARY:...> may only be used with binary targets to specify link
libraries.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
@@ -0,0 +1,4 @@
enable_language(C)
add_library(empty SHARED empty.c)
target_link_options(empty PRIVATE $<LINK_LIBRARY:FEAT>)
@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.1...3.22)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)
@@ -0,0 +1,4 @@
if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}")
set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base1> --LIBFLAG<base2>'.")
endif()
@@ -0,0 +1,4 @@
if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<base2> --SUFFIXGROUP'.")
endif()
@@ -0,0 +1,4 @@
if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP <base1> <other> --SUFFIXGROUP'.")
endif()

Some files were not shown because too many files have changed in this diff Show More