summaryrefslogtreecommitdiffstats
path: root/Include/graminit.h
blob: 0596fb18534bab07987cb8c21e7577d42f97e98b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#define single_input 256
#define file_input 257
#define eval_input 258
#define decorator 259
#define decorators 260
#define funcdef 261
#define parameters 262
#define typedargslist 263
#define tname 264
#define tfpdef 265
#define tfplist 266
#define varargslist 267
#define vname 268
#define vfpdef 269
#define vfplist 270
#define stmt 271
#define simple_stmt 272
#define small_stmt 273
#define expr_stmt 274
#define augassign 275
#define del_stmt 276
#define pass_stmt 277
#define flow_stmt 278
#define break_stmt 279
#define continue_stmt 280
#define return_stmt 281
#define yield_stmt 282
#define raise_stmt 283
#define import_stmt 284
#define import_name 285
#define import_from 286
#define import_as_name 287
#define dotted_as_name 288
#define import_as_names 289
#define dotted_as_names 290
#define dotted_name 291
#define global_stmt 292
#define nonlocal_stmt 293
#define assert_stmt 294
#define compound_stmt 295
#define if_stmt 296
#define while_stmt 297
#define for_stmt 298
#define try_stmt 299
#define with_stmt 300
#define with_var 301
#define except_clause 302
#define suite 303
#define test 304
#define test_nocond 305
#define lambdef 306
#define lambdef_nocond 307
#define or_test 308
#define and_test 309
#define not_test 310
#define comparison 311
#define comp_op 312
#define expr 313
#define xor_expr 314
#define and_expr 315
#define shift_expr 316
#define arith_expr 317
#define term 318
#define factor 319
#define power 320
#define atom 321
#define testlist_comp 322
#define trailer 323
#define subscriptlist 324
#define subscript 325
#define sliceop 326
#define exprlist 327
#define testlist 328
#define dictorsetmaker 329
#define classdef 330
#define arglist 331
#define argument 332
#define comp_iter 333
#define comp_for 334
#define comp_if 335
#define testlist1 336
#define encoding_decl 337
#define yield_expr 338
>-rw-r--r--Source/cmGeneratorTarget.cxx21
-rw-r--r--Source/cmGeneratorTarget.h3
-rw-r--r--Source/cmGlobalXCodeGenerator.cxx4
-rw-r--r--Source/cmLinkLibrariesCommand.cxx2
-rw-r--r--Source/cmLinkLineComputer.cxx10
-rw-r--r--Source/cmLinkLineDeviceComputer.cxx8
-rw-r--r--Source/cmListFileCache.cxx10
-rw-r--r--Source/cmListFileCache.h14
-rw-r--r--Source/cmLocalGenerator.cxx4
-rw-r--r--Source/cmLocalVisualStudio7Generator.cxx4
-rw-r--r--Source/cmMacroCommand.cxx2
-rw-r--r--Source/cmMakefile.cxx37
-rw-r--r--Source/cmMakefile.h1
-rw-r--r--Source/cmPlaceholderExpander.cxx54
-rw-r--r--Source/cmPlaceholderExpander.h19
-rw-r--r--Source/cmRulePlaceholderExpander.cxx245
-rw-r--r--Source/cmRulePlaceholderExpander.h14
-rw-r--r--Source/cmTarget.cxx140
-rw-r--r--Source/cmTargetLinkLibrariesCommand.cxx3
-rw-r--r--Source/cmVariableWatchCommand.cxx2
-rw-r--r--Source/cmVisualStudio10TargetGenerator.cxx10
-rw-r--r--Source/cmake.cxx2
-rw-r--r--Source/cmcmd.cxx23
34 files changed, 1176 insertions, 382 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index ddcdd7e..d6e0096 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -358,6 +358,8 @@ set(SRCS
cmLocalCommonGenerator.h
cmLocalGenerator.cxx
cmLocalGenerator.h
+ cmPlaceholderExpander.cxx
+ cmPlaceholderExpander.h
cmRulePlaceholderExpander.cxx
cmRulePlaceholderExpander.h
cmLocalUnixMakefileGenerator3.cxx
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index b07c051..2e1b9fd 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,8 +1,8 @@
# CMake version number components.
set(CMake_VERSION_MAJOR 3)
set(CMake_VERSION_MINOR 23)
-set(CMake_VERSION_PATCH 0)
-set(CMake_VERSION_RC 1)
+set(CMake_VERSION_PATCH 20220211)
+#set(CMake_VERSION_RC 0)
set(CMake_VERSION_IS_DIRTY 0)
# Start with the full version number used in tags. It has no dev info.
diff --git a/Source/cmCMakeLanguageCommand.cxx b/Source/cmCMakeLanguageCommand.cxx
index 789c78d..27d8cb8 100644
--- a/Source/cmCMakeLanguageCommand.cxx
+++ b/Source/cmCMakeLanguageCommand.cxx
@@ -84,7 +84,8 @@ bool cmCMakeLanguageCommandCALL(std::vector<cmListFileArgument> const& args,
for (size_t i = startArg; i < args.size(); ++i) {
funcArgs.emplace_back(args[i].Value, args[i].Delim, context.Line);
}
- cmListFileFunction func{ callCommand, context.Line, std::move(funcArgs) };
+ cmListFileFunction func{ callCommand, context.Line, context.Line,
+ std::move(funcArgs) };
if (defer) {
if (defer->Id.empty()) {
diff --git a/Source/cmCPluginAPI.cxx b/Source/cmCPluginAPI.cxx
index 1b11f20..abec968 100644
--- a/Source/cmCPluginAPI.cxx
+++ b/Source/cmCPluginAPI.cxx
@@ -432,7 +432,7 @@ static int CCONV cmExecuteCommand(void* arg, const char* name, int numArgs,
lffArgs.emplace_back(args[i], cmListFileArgument::Quoted, 0);
}
- cmListFileFunction lff{ name, 0, std::move(lffArgs) };
+ cmListFileFunction lff{ name, 0, 0, std::move(lffArgs) };
cmExecutionStatus status(*mf);
return mf->ExecuteCommand(lff, status);
}
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx
index 370ddff..e6073cb 100644
--- a/Source/cmComputeLinkDepends.cxx
+++ b/Source/cmComputeLinkDepends.cxx
@@ -11,13 +11,18 @@
#include <utility>
#include <cm/memory>
+#include <cm/string_view>
+#include <cmext/string_view>
#include "cmComputeComponentGraph.h"
+#include "cmGeneratorExpression.h"
+#include "cmGeneratorExpressionDAGChecker.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
#include "cmListFileCache.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
+#include "cmMessageType.h"
#include "cmRange.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
@@ -174,8 +179,35 @@ 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();
+}
+}
+
+const std::string cmComputeLinkDepends::LinkEntry::DEFAULT = "DEFAULT";
+
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 +215,50 @@ cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
this->GlobalGenerator =
this->Target->GetLocalGenerator()->GetGlobalGenerator();
this->CMakeInstance = this->GlobalGenerator->GetCMakeInstance();
+ this->LinkLanguage = linkLanguage;
+
+ // target oriented feature override property takes precedence over
+ // global override property
+ cm::string_view lloPrefix = "LINK_LIBRARY_OVERRIDE_"_s;
+ auto const& keys = this->Target->GetPropertyKeys();
+ std::for_each(
+ keys.cbegin(), keys.cend(),
+ [this, &lloPrefix, &config, &linkLanguage](std::string const& key) {
+ if (cmHasPrefix(key, lloPrefix)) {
+ if (cmValue feature = this->Target->GetProperty(key)) {
+ if (!feature->empty() && key.length() > lloPrefix.length()) {
+ auto item = key.substr(lloPrefix.length());
+ cmGeneratorExpressionDAGChecker dag{ this->Target->GetBacktrace(),
+ this->Target,
+ "LINK_LIBRARY_OVERRIDE",
+ nullptr, nullptr };
+ auto overrideFeature = cmGeneratorExpression::Evaluate(
+ feature, this->Target->GetLocalGenerator(), config, this->Target,
+ &dag, this->Target, linkLanguage);
+ this->LinkLibraryOverride.emplace(item, overrideFeature);
+ }
+ }
+ }
+ });
+ // global override property
+ if (cmValue linkLibraryOverride =
+ this->Target->GetProperty("LINK_LIBRARY_OVERRIDE")) {
+ cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target,
+ "LINK_LIBRARY_OVERRIDE", nullptr,
+ nullptr };
+ auto overrideValue = cmGeneratorExpression::Evaluate(
+ linkLibraryOverride, target->GetLocalGenerator(), config, target, &dag,
+ target, linkLanguage);
+
+ auto overrideList = cmTokenize(overrideValue, ","_s);
+ if (overrideList.size() >= 2) {
+ auto const& feature = overrideList.front();
+ for_each(overrideList.cbegin() + 1, overrideList.cend(),
+ [this, &feature](std::string const& item) {
+ this->LinkLibraryOverride.emplace(item, feature);
+ });
+ }
+ }
// The configuration being linked.
this->HasConfig = !config.empty();
@@ -249,7 +325,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 +357,40 @@ cmComputeLinkDepends::Compute()
return this->FinalLinkEntries;
}
-std::map<cmLinkItem, int>::iterator cmComputeLinkDepends::AllocateLinkEntry(
- cmLinkItem const& item)
+std::string const& cmComputeLinkDepends::GetCurrentFeature(
+ std::string const& item, std::string const& defaultFeature) const
+{
+ auto it = this->LinkLibraryOverride.find(item);
+ return it == this->LinkLibraryOverride.end() ? defaultFeature : it->second;
+}
+
+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 +416,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 +506,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 +524,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 +623,7 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index,
{
// Track inferred dependency sets implied by this list.
std::map<int, DependSet> dependSets;
+ std::string feature = LinkEntry::DEFAULT;
// Loop over the libraries linked directly by the depender.
for (T const& l : libs) {
@@ -550,9 +633,89 @@ 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 = LinkEntry::DEFAULT;
+ 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];
+ auto const& itemFeature =
+ this->GetCurrentFeature(entry.Item.Value, feature);
+ if (itemFeature != LinkEntry::DEFAULT) {
+ 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 = itemFeature;
+ }
+ }
+ }
+
+ bool supportedItem = entry.Target == nullptr ||
+ (entry.Target->GetType() != cmStateEnums::TargetType::OBJECT_LIBRARY &&
+ entry.Target->GetType() != cmStateEnums::TargetType::INTERFACE_LIBRARY);
+
+ if (supportedItem && entry.Feature != itemFeature) {
+ // 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 ",
+ (itemFeature == LinkEntry::DEFAULT
+ ? "without any feature or 'DEFAULT' feature"
+ : cmStrCat("with the feature '", itemFeature, '\'')),
+ ", has already occurred ",
+ (entry.Feature == LinkEntry::DEFAULT
+ ? "without any feature or 'DEFAULT' 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 +830,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 +1032,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 != LinkEntry::DEFAULT) {
+ fprintf(stderr, ", feature [%s]", lei.Feature.c_str());
}
+ fprintf(stderr, "\n");
}
fprintf(stderr, "\n");
}
diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h
index 72316f1..64603e0 100644
--- a/Source/cmComputeLinkDepends.h
+++ b/Source/cmComputeLinkDepends.h
@@ -9,6 +9,7 @@
#include <queue>
#include <set>
#include <string>
+#include <utility>
#include <vector>
#include "cmGraphAdjacencyList.h"
@@ -29,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;
@@ -38,11 +40,23 @@ public:
// Basic information about each link item.
struct LinkEntry
{
+ LinkEntry() = default;
+ LinkEntry(BT<std::string> item, cmGeneratorTarget const* target = nullptr)
+ : Item(std::move(item))
+ , Target(target)
+ {
+ }
+
+ static const std::string DEFAULT;
+
BT<std::string> Item;
cmGeneratorTarget const* Target = nullptr;
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 = std::string(DEFAULT);
};
using EntryVector = std::vector<LinkEntry>;
@@ -60,12 +74,17 @@ private:
cmMakefile* Makefile;
cmGlobalGenerator const* GlobalGenerator;
cmake* CMakeInstance;
+ std::string LinkLanguage;
std::string Config;
EntryVector FinalLinkEntries;
+ std::map<std::string, std::string> LinkLibraryOverride;
+
+ std::string const& GetCurrentFeature(
+ std::string const& item, std::string const& defaultFeature) const;
- 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();
@@ -123,7 +142,7 @@ private:
void DisplayConstraintGraph();
// Ordering algorithm.
- void OrderLinkEntires();
+ void OrderLinkEntries();
std::vector<char> ComponentVisited;
std::vector<int> ComponentOrder;
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx
index 2ff91fe..15e9d60 100644
--- a/Source/cmComputeLinkInformation.cxx
+++ b/Source/cmComputeLinkInformation.cxx
@@ -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");
@@ -409,6 +431,10 @@ cmComputeLinkInformation::cmComputeLinkInformation(
cmComputeLinkInformation::~cmComputeLinkInformation() = default;
+namespace {
+const std::string& DEFAULT = cmComputeLinkDepends::LinkEntry::DEFAULT;
+}
+
void cmComputeLinkInformation::AppendValues(
std::string& result, std::vector<BT<std::string>>& values)
{
@@ -510,17 +536,54 @@ 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 != DEFAULT &&
+ (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.Item, linkEntry.Target);
+ this->AddSharedDepItem(linkEntry);
} else {
- this->AddItem(linkEntry.Item, linkEntry.Target,
- linkEntry.IsObject ? ItemIsObject::Yes : ItemIsObject::No);
+ this->AddItem(linkEntry);
+ }
+ }
+
+ 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);
}
}
@@ -576,6 +639,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.
@@ -610,7 +937,7 @@ void cmComputeLinkInformation::AddRuntimeLinkLibrary(std::string const& lang)
std::vector<std::string> libsVec = cmExpandedList(*runtimeLinkOptions);
for (std::string const& i : libsVec) {
if (!cm::contains(this->ImplicitLinkLibs, i)) {
- this->AddItem(i, nullptr);
+ this->AddItem({ i });
}
}
}
@@ -625,7 +952,7 @@ void cmComputeLinkInformation::AddImplicitLinkInfo(std::string const& lang)
std::vector<std::string> libsVec = cmExpandedList(*libs);
for (std::string const& i : libsVec) {
if (!cm::contains(this->ImplicitLinkLibs, i)) {
- this->AddItem(i, nullptr);
+ this->AddItem({ i });
}
}
}
@@ -639,10 +966,11 @@ void cmComputeLinkInformation::AddImplicitLinkInfo(std::string const& lang)
}
}
-void cmComputeLinkInformation::AddItem(BT<std::string> const& item,
- cmGeneratorTarget const* tgt,
- ItemIsObject isObject)
+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());
@@ -657,28 +985,27 @@ void cmComputeLinkInformation::AddItem(BT<std::string> const& item,
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 == 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, 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);
if (!libName.empty()) {
- this->AddItem(BT<std::string>(libName, item.Backtrace), nullptr);
+ this->AddItem(BT<std::string>(libName, item.Backtrace));
}
} else if (tgt->GetType() == cmStateEnums::OBJECT_LIBRARY) {
// Ignore object library!
@@ -706,7 +1033,9 @@ void cmComputeLinkInformation::AddItem(BT<std::string> const& item,
this->Depends.push_back(lib.Value);
}
- this->AddTargetItem(lib, tgt);
+ LinkEntry libEntry{ entry };
+ libEntry.Item = lib;
+ this->AddTargetItem(libEntry);
this->AddLibraryRuntimeInfo(lib.Value, tgt);
if (tgt && tgt->GetType() == cmStateEnums::SHARED_LIBRARY &&
this->Target->IsDLLPlatform()) {
@@ -719,26 +1048,28 @@ void cmComputeLinkInformation::AddItem(BT<std::string> const& item,
if (cmSystemTools::IsPathToFramework(item.Value) &&
this->Makefile->IsOn("APPLE")) {
// This is a framework.
- this->AddFrameworkItem(item.Value);
+ this->AddFrameworkItem(entry);
} else 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(item, isObject);
+ this->AddFullItem(entry);
this->AddLibraryRuntimeInfo(item.Value);
}
} else {
// This is a library or option specified by the user.
- this->AddUserItem(item, true);
+ this->AddUserItem(entry, true);
}
}
}
-void cmComputeLinkInformation::AddSharedDepItem(BT<std::string> const& item,
- const cmGeneratorTarget* tgt)
+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() &&
@@ -776,7 +1107,7 @@ void cmComputeLinkInformation::AddSharedDepItem(BT<std::string> const& item,
// If in linking mode, just link to the shared library.
if (this->SharedDependencyMode == SharedDepModeLink) {
- this->AddItem(item, tgt);
+ this->AddItem(entry);
return;
}
@@ -1058,8 +1389,7 @@ void cmComputeLinkInformation::SetCurrentLinkType(LinkType lt)
}
}
-void cmComputeLinkInformation::AddTargetItem(BT<std::string> const& item,
- cmGeneratorTarget const* target)
+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
@@ -1067,6 +1397,9 @@ void cmComputeLinkInformation::AddTargetItem(BT<std::string> const& item,
// 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);
}
@@ -1079,7 +1412,7 @@ void cmComputeLinkInformation::AddTargetItem(BT<std::string> const& item,
// Handle case of an imported shared library with no soname.
if (this->NoSONameUsesPath &&
target->IsImportedSharedLibWithoutSOName(this->Config)) {
- this->AddSharedLibNoSOName(item.Value);
+ this->AddSharedLibNoSOName(entry);
return;
}
@@ -1092,19 +1425,23 @@ void cmComputeLinkInformation::AddTargetItem(BT<std::string> const& item,
}
// 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 == DEFAULT
+ ? "__CMAKE_LINK_LIBRARY"
+ : entry.Feature));
}
-void cmComputeLinkInformation::AddFullItem(BT<std::string> const& item,
- ItemIsObject isObject)
+void cmComputeLinkInformation::AddFullItem(LinkEntry const& entry)
{
+ BT<std::string> const& item = entry.Item;
+
// Check for the implicit link directory special case.
- if (this->CheckImplicitDirItem(item.Value)) {
+ if (this->CheckImplicitDirItem(entry)) {
return;
}
// Check for case of shared library with no builtin soname.
- if (this->NoSONameUsesPath && this->CheckSharedLibNoSOName(item.Value)) {
+ if (this->NoSONameUsesPath && this->CheckSharedLibNoSOName(entry)) {
return;
}
@@ -1116,7 +1453,7 @@ void cmComputeLinkInformation::AddFullItem(BT<std::string> const& item,
generator.find("Xcode") != std::string::npos)) {
std::string file = cmSystemTools::GetFilenameName(item.Value);
if (!this->ExtractAnyLibraryName.find(file)) {
- this->HandleBadFullItem(item.Value, file);
+ this->HandleBadFullItem(entry, file);
return;
}
}
@@ -1147,11 +1484,18 @@ void cmComputeLinkInformation::AddFullItem(BT<std::string> const& item,
}
// Now add the full path to the library.
- this->Items.emplace_back(item, ItemIsPath::Yes, isObject);
+ this->Items.emplace_back(
+ item, ItemIsPath::Yes, nullptr,
+ this->FindLibraryFeature(
+ entry.Feature == DEFAULT
+ ? (entry.IsObject ? "__CMAKE_LINK_OBJECT" : "__CMAKE_LINK_LIBRARY")
+ : entry.Feature));
}
-bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item)
+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.
@@ -1160,7 +1504,7 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item)
}
// Check if this item is in an implicit link directory.
- std::string dir = cmSystemTools::GetFilenamePath(item);
+ std::string dir = cmSystemTools::GetFilenamePath(item.Value);
if (!cm::contains(this->ImplicitLinkDirs, dir)) {
// Only libraries in implicit link directories are converted to
// pathless items.
@@ -1169,7 +1513,7 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item)
// Only apply the policy below if the library file is one that can
// be found by the linker.
- std::string file = cmSystemTools::GetFilenameName(item);
+ std::string file = cmSystemTools::GetFilenameName(item.Value);
if (!this->ExtractAnyLibraryName.find(file)) {
return false;
}
@@ -1179,10 +1523,10 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item)
case cmPolicies::WARN:
if (this->CMP0060Warn) {
// Print the warning at most once for this item.
- std::string const& wid = "CMP0060-WARNING-GIVEN-" + item;
+ std::string const& wid = "CMP0060-WARNING-GIVEN-" + item.Value;
if (!this->CMakeInstance->GetPropertyAsBool(wid)) {
this->CMakeInstance->SetProperty(wid, "1");
- this->CMP0060WarnItems.insert(item);
+ this->CMP0060WarnItems.insert(item.Value);
}
}
CM_FALLTHROUGH;
@@ -1200,15 +1544,17 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item)
// directory then just report the file name without the directory
// portion. This will allow the system linker to locate the proper
// library for the architecture at link time.
- this->AddUserItem(file, false);
+ LinkEntry fileEntry{ entry };
+ fileEntry.Item = file;
+ this->AddUserItem(fileEntry, false);
// Make sure the link directory ordering will find the library.
- this->OrderLinkerSearchPath->AddLinkLibrary(item);
+ this->OrderLinkerSearchPath->AddLinkLibrary(item.Value);
return true;
}
-void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item,
+void cmComputeLinkInformation::AddUserItem(LinkEntry const& entry,
bool pathNotKnown)
{
// This is called to handle a link item that does not match a CMake
@@ -1219,8 +1565,10 @@ void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item,
// foo ==> -lfoo
// libfoo.a ==> -Wl,-Bstatic -lfoo
- // Pass flags through untouched.
+ 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
@@ -1305,9 +1653,20 @@ void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item,
}
// 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 != 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
@@ -1315,8 +1674,10 @@ void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item,
// specification.
}
-void cmComputeLinkInformation::AddFrameworkItem(std::string const& item)
+void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry)
{
+ std::string const& item = entry.Item.Value;
+
// Try to separate the framework name and path.
if (!this->SplitFramework.find(item)) {
std::ostringstream e;
@@ -1355,10 +1716,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);
}
@@ -1390,42 +1751,44 @@ void cmComputeLinkInformation::AddFrameworkPath(std::string const& p)
}
}
-bool cmComputeLinkInformation::CheckSharedLibNoSOName(std::string const& item)
+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(item);
+ 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(item, soname)) {
- this->AddSharedLibNoSOName(item);
+ if (!cmSystemTools::GuessLibrarySOName(entry.Item.Value, soname)) {
+ this->AddSharedLibNoSOName(entry);
return true;
}
}
return false;
}
-void cmComputeLinkInformation::AddSharedLibNoSOName(std::string const& item)
+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.
- std::string file = cmSystemTools::GetFilenameName(item);
- this->AddUserItem(file, false);
+ 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(item);
+ this->OrderLinkerSearchPath->AddLinkLibrary(entry.Item.Value);
}
-void cmComputeLinkInformation::HandleBadFullItem(std::string const& item,
+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()) {
@@ -1435,7 +1798,9 @@ void cmComputeLinkInformation::HandleBadFullItem(std::string const& item,
// Tell the linker to search for the item and provide the proper
// path for it. Do not contribute to any CMP0003 warning (do not
// put in OldLinkDirItems or OldUserFlagItems).
- this->AddUserItem(file, false);
+ LinkEntry fileEntry{ entry };
+ fileEntry.Item = file;
+ this->AddUserItem(fileEntry, false);
this->OrderLinkerSearchPath->AddLinkLibrary(item);
// Produce any needed message.
@@ -1781,8 +2146,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);
}
}
diff --git a/Source/cmComputeLinkInformation.h b/Source/cmComputeLinkInformation.h
index 0315540..ce9f393 100644
--- a/Source/cmComputeLinkInformation.h
+++ b/Source/cmComputeLinkInformation.h
@@ -5,6 +5,7 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include <iosfwd>
+#include <map>
#include <memory>
#include <set>
#include <string>
@@ -13,6 +14,7 @@
#include "cmsys/RegularExpression.hxx"
+#include "cmComputeLinkDepends.h"
#include "cmListFileCache.h"
#include "cmValue.h"
@@ -27,6 +29,9 @@ class cmake;
*/
class cmComputeLinkInformation
{
+private:
+ class FeatureDescriptor;
+
public:
cmComputeLinkInformation(cmGeneratorTarget const* target,
const std::string& config);
@@ -42,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);
@@ -104,10 +114,10 @@ public:
const cmGeneratorTarget* GetTarget() { return this->Target; }
private:
- void AddItem(BT<std::string> const& item, const cmGeneratorTarget* tgt,
- ItemIsObject isObject = ItemIsObject::No);
- void AddSharedDepItem(BT<std::string> const& item,
- cmGeneratorTarget const* tgt);
+ using LinkEntry = cmComputeLinkDepends::LinkEntry;
+
+ void AddItem(LinkEntry const& entry);
+ void AddSharedDepItem(LinkEntry const& entry);
void AddRuntimeDLL(cmGeneratorTarget const* tgt);
// Output information.
@@ -181,16 +191,15 @@ private:
std::string NoCaseExpression(std::string const& str);
// Handling of link items.
- void AddTargetItem(BT<std::string> const& item,
- const cmGeneratorTarget* target);
- void AddFullItem(BT<std::string> const& item, ItemIsObject isObject);
- bool CheckImplicitDirItem(std::string const& item);
- void AddUserItem(BT<std::string> const& item, bool pathNotKnown);
- void AddFrameworkItem(std::string const& item);
+ void AddTargetItem(LinkEntry const& entry);
+ void AddFullItem(LinkEntry const& entry);
+ bool CheckImplicitDirItem(LinkEntry const& entry);
+ void AddUserItem(LinkEntry const& entry, bool pathNotKnown);
+ void AddFrameworkItem(LinkEntry const& entry);
void DropDirectoryItem(BT<std::string> const& item);
- bool CheckSharedLibNoSOName(std::string const& item);
- void AddSharedLibNoSOName(std::string const& item);
- void HandleBadFullItem(std::string const& item, std::string const& file);
+ bool CheckSharedLibNoSOName(LinkEntry const& entry);
+ void AddSharedLibNoSOName(LinkEntry const& entry);
+ void HandleBadFullItem(LinkEntry const& entry, std::string const& file);
// Framework info.
void ComputeFrameworkInfo();
@@ -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;
};
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index b46b933..a21e614 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -934,13 +934,13 @@ void cmExportFileGenerator::GeneratePolicyHeaderCode(std::ostream& os)
// Isolate the file policy level.
// Support CMake versions as far back as 2.6 but also support using NEW
- // policy settings for up to CMake 3.21 (this upper limit may be reviewed
+ // policy settings for up to CMake 3.22 (this upper limit may be reviewed
// and increased from time to time). This reduces the opportunity for CMake
// warnings when an older export file is later used with newer CMake
// versions.
/* clang-format off */
os << "cmake_policy(PUSH)\n"
- << "cmake_policy(VERSION 2.6...3.21)\n";
+ << "cmake_policy(VERSION 2.6...3.22)\n";
/* clang-format on */
}
diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx
index d4b02a5..d35d428 100644
--- a/Source/cmGeneratorExpressionDAGChecker.cxx
+++ b/Source/cmGeneratorExpressionDAGChecker.cxx
@@ -167,7 +167,7 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkExpression() const
cm::string_view property(this->Top()->Property);
return property == "LINK_DIRECTORIES"_s || property == "LINK_OPTIONS"_s ||
- property == "LINK_DEPENDS"_s;
+ property == "LINK_DEPENDS"_s || property == "LINK_LIBRARY_OVERRIDE"_s;
}
bool cmGeneratorExpressionDAGChecker::EvaluatingLinkOptionsExpression() const
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index 396e9c9..b63b90b 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -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 }
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 9f1029e..e722fa9 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -2047,7 +2047,11 @@ void cmGeneratorTarget::ComputeKindedSources(KindedSources& files,
} else if (ext == "appxmanifest") {
kind = SourceKindAppManifest;
} else if (ext == "manifest") {
- kind = SourceKindManifest;
+ if (sf->GetPropertyAsBool("VS_DEPLOYMENT_CONTENT")) {
+ kind = SourceKindExtra;
+ } else {
+ kind = SourceKindManifest;
+ }
} else if (ext == "pfx") {
kind = SourceKindCertificate;
} else if (ext == "xaml") {
@@ -4625,7 +4629,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 +4689,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 +6389,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;
}
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index 45639c0..3e30913 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -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,
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index 203addd..489c7fb 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -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)) {
diff --git a/Source/cmLinkLibrariesCommand.cxx b/Source/cmLinkLibrariesCommand.cxx
index 2b8f836..ed89e91 100644
--- a/Source/cmLinkLibrariesCommand.cxx
+++ b/Source/cmLinkLibrariesCommand.cxx
@@ -35,5 +35,7 @@ bool cmLinkLibrariesCommand(std::vector<std::string> const& args,
mf.AppendProperty("LINK_LIBRARIES", *i);
}
+ mf.CheckProperty("LINK_LIBRARIES");
+
return true;
}
diff --git a/Source/cmLinkLineComputer.cxx b/Source/cmLinkLineComputer.cxx
index 5646368..290642b 100644
--- a/Source/cmLinkLineComputer.cxx
+++ b/Source/cmLinkLineComputer.cxx
@@ -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;
}
diff --git a/Source/cmLinkLineDeviceComputer.cxx b/Source/cmLinkLineDeviceComputer.cxx
index 43f161b..71f9f80 100644
--- a/Source/cmLinkLineDeviceComputer.cxx
+++ b/Source/cmLinkLineDeviceComputer.cxx
@@ -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) {
diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx
index 3da266d..b90af08 100644
--- a/Source/cmListFileCache.cxx
+++ b/Source/cmListFileCache.cxx
@@ -40,6 +40,7 @@ struct cmListFileParser
cmListFileLexer* Lexer;
std::string FunctionName;
long FunctionLine;
+ long FunctionLineEnd;
std::vector<cmListFileArgument> FunctionArguments;
enum
{
@@ -146,7 +147,7 @@ bool cmListFileParser::Parse()
if (this->ParseFunction(token->text, token->line)) {
this->ListFile->Functions.emplace_back(
std::move(this->FunctionName), this->FunctionLine,
- std::move(this->FunctionArguments));
+ this->FunctionLineEnd, std::move(this->FunctionArguments));
} else {
return false;
}
@@ -259,6 +260,7 @@ bool cmListFileParser::ParseFunction(const char* name, long line)
}
} else if (token->type == cmListFileLexer_Token_ParenRight) {
if (parenDepth == 0) {
+ this->FunctionLineEnd = token->line;
return true;
}
parenDepth--;
@@ -539,11 +541,11 @@ std::ostream& operator<<(std::ostream& os, BT<std::string> const& s)
return os << s.Value;
}
-std::vector<BT<std::string>> ExpandListWithBacktrace(
- std::string const& list, cmListFileBacktrace const& bt)
+std::vector<BT<std::string>> cmExpandListWithBacktrace(
+ std::string const& list, cmListFileBacktrace const& bt, bool emptyArgs)
{
std::vector<BT<std::string>> result;
- std::vector<std::string> tmp = cmExpandedList(list);
+ std::vector<std::string> tmp = cmExpandedList(list, emptyArgs);
result.reserve(tmp.size());
for (std::string& i : tmp) {
result.emplace_back(std::move(i), bt);
diff --git a/Source/cmListFileCache.h b/Source/cmListFileCache.h
index 5d45027..c3da81b 100644
--- a/Source/cmListFileCache.h
+++ b/Source/cmListFileCache.h
@@ -51,9 +51,9 @@ struct cmListFileArgument
class cmListFileFunction
{
public:
- cmListFileFunction(std::string name, long line,
+ cmListFileFunction(std::string name, long line, long lineEnd,
std::vector<cmListFileArgument> args)
- : Impl{ std::make_shared<Implementation>(std::move(name), line,
+ : Impl{ std::make_shared<Implementation>(std::move(name), line, lineEnd,
std::move(args)) }
{
}
@@ -69,6 +69,7 @@ public:
}
long Line() const noexcept { return this->Impl->Line; }
+ long LineEnd() const noexcept { return this->Impl->LineEnd; }
std::vector<cmListFileArgument> const& Arguments() const noexcept
{
@@ -78,11 +79,12 @@ public:
private:
struct Implementation
{
- Implementation(std::string name, long line,
+ Implementation(std::string name, long line, long lineEnd,
std::vector<cmListFileArgument> args)
: OriginalName{ std::move(name) }
, LowerCaseName{ cmSystemTools::LowerCase(this->OriginalName) }
, Line{ line }
+ , LineEnd{ lineEnd }
, Arguments{ std::move(args) }
{
}
@@ -90,6 +92,7 @@ private:
std::string OriginalName;
std::string LowerCaseName;
long Line = 0;
+ long LineEnd = 0;
std::vector<cmListFileArgument> Arguments;
};
@@ -230,9 +233,10 @@ public:
friend bool operator==(T const& l, BTs<T> const& r) { return l == r.Value; }
};
-std::vector<BT<std::string>> ExpandListWithBacktrace(
+std::vector<BT<std::string>> cmExpandListWithBacktrace(
std::string const& list,
- cmListFileBacktrace const& bt = cmListFileBacktrace());
+ cmListFileBacktrace const& bt = cmListFileBacktrace(),
+ bool emptyArgs = false);
struct cmListFile
{
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 2adb232..3976c42 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -3199,7 +3199,7 @@ void cmLocalGenerator::AppendDefines(std::set<std::string>& defines,
std::string const& defines_list) const
{
std::set<BT<std::string>> tmp;
- this->AppendDefines(tmp, ExpandListWithBacktrace(defines_list));
+ this->AppendDefines(tmp, cmExpandListWithBacktrace(defines_list));
for (BT<std::string> const& i : tmp) {
defines.emplace(i.Value);
}
@@ -3214,7 +3214,7 @@ void cmLocalGenerator::AppendDefines(std::set<BT<std::string>>& defines,
}
// Expand the list of definitions.
- this->AppendDefines(defines, ExpandListWithBacktrace(defines_list));
+ this->AppendDefines(defines, cmExpandListWithBacktrace(defines_list));
}
void cmLocalGenerator::AppendDefines(
diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx
index ed7e888..f65add1 100644
--- a/Source/cmLocalVisualStudio7Generator.cxx
+++ b/Source/cmLocalVisualStudio7Generator.cxx
@@ -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 << " ";
diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx
index 154df63..ef12487 100644
--- a/Source/cmMacroCommand.cxx
+++ b/Source/cmMacroCommand.cxx
@@ -116,7 +116,7 @@ bool cmMacroHelperCommand::operator()(
newLFFArgs.push_back(std::move(arg));
}
cmListFileFunction newLFF{ func.OriginalName(), func.Line(),
- std::move(newLFFArgs) };
+ func.LineEnd(), std::move(newLFFArgs) };
cmExecutionStatus status(makefile);
if (!makefile.ExecuteCommand(newLFF, status) || status.GetNestedError()) {
// The error message should have already included the call stack
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 94d3be6..6a5d518 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -291,6 +291,9 @@ void cmMakefile::PrintCommandTrace(
builder["indentation"] = "";
val["file"] = full_path;
val["line"] = static_cast<Json::Value::Int64>(lff.Line());
+ if (lff.Line() != lff.LineEnd()) {
+ val["line_end"] = static_cast<Json::Value::Int64>(lff.LineEnd());
+ }
if (deferId) {
val["defer"] = *deferId;
}
@@ -302,6 +305,8 @@ void cmMakefile::PrintCommandTrace(
val["time"] = cmSystemTools::GetTime();
val["frame"] =
static_cast<Json::Value::UInt64>(this->ExecutionStatusStack.size());
+ val["global_frame"] =
+ static_cast<Json::Value::UInt64>(this->RecursionDepth);
msg << Json::writeString(builder, val);
#endif
break;
@@ -1663,6 +1668,7 @@ void cmMakefile::Configure()
this->Backtrace);
cmListFileFunction project{ "project",
0,
+ 0,
{ { "Project", cmListFileArgument::Unquoted,
0 },
{ "__CMAKE_INJECTED_PROJECT_COMMAND__",
@@ -3976,6 +3982,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);
@@ -4395,12 +4426,14 @@ bool cmMakefile::SetPolicy(cmPolicies::PolicyID id,
}
// Deprecate old policies.
- if (status == cmPolicies::OLD && id <= cmPolicies::CMP0094 &&
+ if (status == cmPolicies::OLD && id <= cmPolicies::CMP0097 &&
!(this->GetCMakeInstance()->GetIsInTryCompile() &&
(
// Policies set by cmCoreTryCompile::TryCompileCode.
id == cmPolicies::CMP0065 || id == cmPolicies::CMP0083 ||
- id == cmPolicies::CMP0091))) {
+ id == cmPolicies::CMP0091)) &&
+ (!this->IsSet("CMAKE_WARN_DEPRECATED") ||
+ this->IsOn("CMAKE_WARN_DEPRECATED"))) {
this->IssueMessage(MessageType::DEPRECATION_WARNING,
cmPolicies::GetPolicyDeprecatedWarning(id));
}
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index f425697..ad8a014 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -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);
diff --git a/Source/cmPlaceholderExpander.cxx b/Source/cmPlaceholderExpander.cxx
new file mode 100644
index 0000000..118017e
--- /dev/null
+++ b/Source/cmPlaceholderExpander.cxx
@@ -0,0 +1,54 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmPlaceholderExpander.h"
+
+#include <cctype>
+
+std::string& cmPlaceholderExpander::ExpandVariables(std::string& s)
+{
+ std::string::size_type start = s.find('<');
+ // no variables to expand
+ if (start == std::string::npos) {
+ return s;
+ }
+ std::string::size_type pos = 0;
+ std::string expandedInput;
+ while (start != std::string::npos && start < s.size() - 2) {
+ std::string::size_type end = s.find('>', start);
+ // if we find a < with no > we are done
+ if (end == std::string::npos) {
+ s = expandedInput;
+ return s;
+ }
+ char c = s[start + 1];
+ // if the next char after the < is not A-Za-z then
+ // skip it and try to find the next < in the string
+ if (!isalpha(c)) {
+ start = s.find('<', start + 1);
+ } else {
+ // extract the var
+ std::string var = s.substr(start + 1, end - start - 1);
+ std::string replace = this->ExpandVariable(var);
+ expandedInput += s.substr(pos, start - pos);
+
+ // Prevent consecutive whitespace in the output if the rule variable
+ // expands to an empty string.
+ bool consecutive = replace.empty() && start > 0 && s[start - 1] == ' ' &&
+ end + 1 < s.size() && s[end + 1] == ' ';
+ if (consecutive) {
+ expandedInput.pop_back();
+ }
+
+ expandedInput += replace;
+
+ // move to next one
+ start = s.find('<', start + var.size() + 2);
+ pos = end + 1;
+ }
+ }
+ // add the rest of the input
+ expandedInput += s.substr(pos, s.size() - pos);
+ s = expandedInput;
+
+ return s;
+}
diff --git a/Source/cmPlaceholderExpander.h b/Source/cmPlaceholderExpander.h
new file mode 100644
index 0000000..24225cc
--- /dev/null
+++ b/Source/cmPlaceholderExpander.h
@@ -0,0 +1,19 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <string>
+
+class cmPlaceholderExpander
+{
+public:
+ virtual ~cmPlaceholderExpander() = default;
+
+ std::string& ExpandVariables(std::string& string);
+
+protected:
+ virtual std::string ExpandVariable(std::string const& variable) = 0;
+};
diff --git a/Source/cmRulePlaceholderExpander.cxx b/Source/cmRulePlaceholderExpander.cxx
index 4cee09d..b63d11c 100644
--- a/Source/cmRulePlaceholderExpander.cxx
+++ b/Source/cmRulePlaceholderExpander.cxx
@@ -2,7 +2,6 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmRulePlaceholderExpander.h"
-#include <cctype>
#include <utility>
#include "cmOutputConverter.h"
@@ -20,195 +19,194 @@ cmRulePlaceholderExpander::cmRulePlaceholderExpander(
{
}
-std::string cmRulePlaceholderExpander::ExpandRuleVariable(
- cmOutputConverter* outputConverter, std::string const& variable,
- const RuleVariables& replaceValues)
+std::string cmRulePlaceholderExpander::ExpandVariable(
+ std::string const& variable)
{
- if (replaceValues.LinkFlags) {
+ if (this->ReplaceValues->LinkFlags) {
if (variable == "LINK_FLAGS") {
- return replaceValues.LinkFlags;
+ return this->ReplaceValues->LinkFlags;
}
}
- if (replaceValues.Manifests) {
+ if (this->ReplaceValues->Manifests) {
if (variable == "MANIFESTS") {
- return replaceValues.Manifests;
+ return this->ReplaceValues->Manifests;
}
}
- if (replaceValues.Flags) {
+ if (this->ReplaceValues->Flags) {
if (variable == "FLAGS") {
- return replaceValues.Flags;
+ return this->ReplaceValues->Flags;
}
}
- if (replaceValues.Source) {
+ if (this->ReplaceValues->Source) {
if (variable == "SOURCE") {
- return replaceValues.Source;
+ return this->ReplaceValues->Source;
}
}
- if (replaceValues.DynDepFile) {
+ if (this->ReplaceValues->DynDepFile) {
if (variable == "DYNDEP_FILE") {
- return replaceValues.DynDepFile;
+ return this->ReplaceValues->DynDepFile;
}
}
- if (replaceValues.PreprocessedSource) {
+ if (this->ReplaceValues->PreprocessedSource) {
if (variable == "PREPROCESSED_SOURCE") {
- return replaceValues.PreprocessedSource;
+ return this->ReplaceValues->PreprocessedSource;
}
}
- if (replaceValues.AssemblySource) {
+ if (this->ReplaceValues->AssemblySource) {
if (variable == "ASSEMBLY_SOURCE") {
- return replaceValues.AssemblySource;
+ return this->ReplaceValues->AssemblySource;
}
}
- if (replaceValues.Object) {
+ if (this->ReplaceValues->Object) {
if (variable == "OBJECT") {
- return replaceValues.Object;
+ return this->ReplaceValues->Object;
}
}
- if (replaceValues.ObjectDir) {
+ if (this->ReplaceValues->ObjectDir) {
if (variable == "OBJECT_DIR") {
- return replaceValues.ObjectDir;
+ return this->ReplaceValues->ObjectDir;
}
}
- if (replaceValues.ObjectFileDir) {
+ if (this->ReplaceValues->ObjectFileDir) {
if (variable == "OBJECT_FILE_DIR") {
- return replaceValues.ObjectFileDir;
+ return this->ReplaceValues->ObjectFileDir;
}
}
- if (replaceValues.Objects) {
+ if (this->ReplaceValues->Objects) {
if (variable == "OBJECTS") {
- return replaceValues.Objects;
+ return this->ReplaceValues->Objects;
}
}
- if (replaceValues.ObjectsQuoted) {
+ if (this->ReplaceValues->ObjectsQuoted) {
if (variable == "OBJECTS_QUOTED") {
- return replaceValues.ObjectsQuoted;
+ return this->ReplaceValues->ObjectsQuoted;
}
}
- if (replaceValues.CudaCompileMode) {
+ if (this->ReplaceValues->CudaCompileMode) {
if (variable == "CUDA_COMPILE_MODE") {
- return replaceValues.CudaCompileMode;
+ return this->ReplaceValues->CudaCompileMode;
}
}
- if (replaceValues.AIXExports) {
+ if (this->ReplaceValues->AIXExports) {
if (variable == "AIX_EXPORTS") {
- return replaceValues.AIXExports;
+ return this->ReplaceValues->AIXExports;
}
}
- if (replaceValues.ISPCHeader) {
+ if (this->ReplaceValues->ISPCHeader) {
if (variable == "ISPC_HEADER") {
- return replaceValues.ISPCHeader;
+ return this->ReplaceValues->ISPCHeader;
}
}
- if (replaceValues.Defines && variable == "DEFINES") {
- return replaceValues.Defines;
+ if (this->ReplaceValues->Defines && variable == "DEFINES") {
+ return this->ReplaceValues->Defines;
}
- if (replaceValues.Includes && variable == "INCLUDES") {
- return replaceValues.Includes;
+ if (this->ReplaceValues->Includes && variable == "INCLUDES") {
+ return this->ReplaceValues->Includes;
}
- if (replaceValues.SwiftLibraryName) {
+ if (this->ReplaceValues->SwiftLibraryName) {
if (variable == "SWIFT_LIBRARY_NAME") {
- return replaceValues.SwiftLibraryName;
+ return this->ReplaceValues->SwiftLibraryName;
}
}
- if (replaceValues.SwiftModule) {
+ if (this->ReplaceValues->SwiftModule) {
if (variable == "SWIFT_MODULE") {
- return replaceValues.SwiftModule;
+ return this->ReplaceValues->SwiftModule;
}
}
- if (replaceValues.SwiftModuleName) {
+ if (this->ReplaceValues->SwiftModuleName) {
if (variable == "SWIFT_MODULE_NAME") {
- return replaceValues.SwiftModuleName;
+ return this->ReplaceValues->SwiftModuleName;
}
}
- if (replaceValues.SwiftOutputFileMap) {
+ if (this->ReplaceValues->SwiftOutputFileMap) {
if (variable == "SWIFT_OUTPUT_FILE_MAP") {
- return replaceValues.SwiftOutputFileMap;
+ return this->ReplaceValues->SwiftOutputFileMap;
}
}
- if (replaceValues.SwiftSources) {
+ if (this->ReplaceValues->SwiftSources) {
if (variable == "SWIFT_SOURCES") {
- return replaceValues.SwiftSources;
+ return this->ReplaceValues->SwiftSources;
}
}
- if (replaceValues.TargetPDB) {
+ if (this->ReplaceValues->TargetPDB) {
if (variable == "TARGET_PDB") {
- return replaceValues.TargetPDB;
+ return this->ReplaceValues->TargetPDB;
}
}
- if (replaceValues.TargetCompilePDB) {
+ if (this->ReplaceValues->TargetCompilePDB) {
if (variable == "TARGET_COMPILE_PDB") {
- return replaceValues.TargetCompilePDB;
+ return this->ReplaceValues->TargetCompilePDB;
}
}
- if (replaceValues.DependencyFile) {
+ if (this->ReplaceValues->DependencyFile) {
if (variable == "DEP_FILE") {
- return replaceValues.DependencyFile;
+ return this->ReplaceValues->DependencyFile;
}
}
- if (replaceValues.DependencyTarget) {
+ if (this->ReplaceValues->DependencyTarget) {
if (variable == "DEP_TARGET") {
- return replaceValues.DependencyTarget;
+ return this->ReplaceValues->DependencyTarget;
}
}
- if (replaceValues.Fatbinary) {
+ if (this->ReplaceValues->Fatbinary) {
if (variable == "FATBINARY") {
- return replaceValues.Fatbinary;
+ return this->ReplaceValues->Fatbinary;
}
}
- if (replaceValues.RegisterFile) {
+ if (this->ReplaceValues->RegisterFile) {
if (variable == "REGISTER_FILE") {
- return replaceValues.RegisterFile;
+ return this->ReplaceValues->RegisterFile;
}
}
- if (replaceValues.Target) {
+ if (this->ReplaceValues->Target) {
if (variable == "TARGET_QUOTED") {
- std::string targetQuoted = replaceValues.Target;
+ std::string targetQuoted = this->ReplaceValues->Target;
if (!targetQuoted.empty() && targetQuoted.front() != '\"') {
targetQuoted = '\"';
- targetQuoted += replaceValues.Target;
+ targetQuoted += this->ReplaceValues->Target;
targetQuoted += '\"';
}
return targetQuoted;
}
if (variable == "TARGET_UNQUOTED") {
- std::string unquoted = replaceValues.Target;
+ std::string unquoted = this->ReplaceValues->Target;
std::string::size_type sz = unquoted.size();
if (sz > 2 && unquoted.front() == '\"' && unquoted.back() == '\"') {
unquoted = unquoted.substr(1, sz - 2);
}
return unquoted;
}
- if (replaceValues.LanguageCompileFlags) {
+ if (this->ReplaceValues->LanguageCompileFlags) {
if (variable == "LANGUAGE_COMPILE_FLAGS") {
- return replaceValues.LanguageCompileFlags;
+ return this->ReplaceValues->LanguageCompileFlags;
}
}
- if (replaceValues.Target) {
+ if (this->ReplaceValues->Target) {
if (variable == "TARGET") {
- return replaceValues.Target;
+ return this->ReplaceValues->Target;
}
}
if (variable == "TARGET_IMPLIB") {
return this->TargetImpLib;
}
if (variable == "TARGET_VERSION_MAJOR") {
- if (replaceValues.TargetVersionMajor) {
- return replaceValues.TargetVersionMajor;
+ if (this->ReplaceValues->TargetVersionMajor) {
+ return this->ReplaceValues->TargetVersionMajor;
}
return "0";
}
if (variable == "TARGET_VERSION_MINOR") {
- if (replaceValues.TargetVersionMinor) {
- return replaceValues.TargetVersionMinor;
+ if (this->ReplaceValues->TargetVersionMinor) {
+ return this->ReplaceValues->TargetVersionMinor;
}
return "0";
}
- if (replaceValues.Target) {
+ if (this->ReplaceValues->Target) {
if (variable == "TARGET_BASE") {
// Strip the last extension off the target name.
- std::string targetBase = replaceValues.Target;
+ std::string targetBase = this->ReplaceValues->Target;
std::string::size_type pos = targetBase.rfind('.');
if (pos != std::string::npos) {
return targetBase.substr(0, pos);
@@ -220,54 +218,54 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable(
if (variable == "TARGET_SONAME" || variable == "SONAME_FLAG" ||
variable == "TARGET_INSTALLNAME_DIR") {
// All these variables depend on TargetSOName
- if (replaceValues.TargetSOName) {
+ if (this->ReplaceValues->TargetSOName) {
if (variable == "TARGET_SONAME") {
- return replaceValues.TargetSOName;
+ return this->ReplaceValues->TargetSOName;
}
- if (variable == "SONAME_FLAG" && replaceValues.SONameFlag) {
- return replaceValues.SONameFlag;
+ if (variable == "SONAME_FLAG" && this->ReplaceValues->SONameFlag) {
+ return this->ReplaceValues->SONameFlag;
}
- if (replaceValues.TargetInstallNameDir &&
+ if (this->ReplaceValues->TargetInstallNameDir &&
variable == "TARGET_INSTALLNAME_DIR") {
- return replaceValues.TargetInstallNameDir;
+ return this->ReplaceValues->TargetInstallNameDir;
}
}
return "";
}
- if (replaceValues.LinkLibraries) {
+ if (this->ReplaceValues->LinkLibraries) {
if (variable == "LINK_LIBRARIES") {
- return replaceValues.LinkLibraries;
+ return this->ReplaceValues->LinkLibraries;
}
}
- if (replaceValues.Language) {
+ if (this->ReplaceValues->Language) {
if (variable == "LANGUAGE") {
- return replaceValues.Language;
+ return this->ReplaceValues->Language;
}
}
- if (replaceValues.CMTargetName) {
+ if (this->ReplaceValues->CMTargetName) {
if (variable == "TARGET_NAME") {
- return replaceValues.CMTargetName;
+ return this->ReplaceValues->CMTargetName;
}
}
- if (replaceValues.CMTargetType) {
+ if (this->ReplaceValues->CMTargetType) {
if (variable == "TARGET_TYPE") {
- return replaceValues.CMTargetType;
+ return this->ReplaceValues->CMTargetType;
}
}
- if (replaceValues.Output) {
+ if (this->ReplaceValues->Output) {
if (variable == "OUTPUT") {
- return replaceValues.Output;
+ return this->ReplaceValues->Output;
}
}
if (variable == "CMAKE_COMMAND") {
- return outputConverter->ConvertToOutputFormat(
+ return this->OutputConverter->ConvertToOutputFormat(
cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
}
auto compIt = this->Compilers.find(variable);
if (compIt != this->Compilers.end()) {
- std::string ret = outputConverter->ConvertToOutputForExisting(
+ std::string ret = this->OutputConverter->ConvertToOutputForExisting(
this->VariableMappings["CMAKE_" + compIt->second + "_COMPILER"]);
std::string const& compilerArg1 =
this->VariableMappings["CMAKE_" + compIt->second + "_COMPILER_ARG1"];
@@ -286,11 +284,12 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable(
this->VariableMappings["CMAKE_" + compIt->second +
"_COMPILE_OPTIONS_SYSROOT"];
- if (compIt->second == replaceValues.Language && replaceValues.Launcher) {
+ if (compIt->second == this->ReplaceValues->Language &&
+ this->ReplaceValues->Launcher) {
// Add launcher as part of expansion so that it always appears
// immediately before the command itself, regardless of whether the
// overall rule template contains other content at the front.
- ret = cmStrCat(replaceValues.Launcher, " ", ret);
+ ret = cmStrCat(this->ReplaceValues->Launcher, " ", ret);
}
// if there are required arguments to the compiler add it
@@ -308,13 +307,14 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable(
!compilerOptionExternalToolchain.empty()) {
ret += " ";
ret += compilerOptionExternalToolchain;
- ret += outputConverter->EscapeForShell(compilerExternalToolchain, true);
+ ret +=
+ this->OutputConverter->EscapeForShell(compilerExternalToolchain, true);
}
std::string sysroot;
// Some platforms may use separate sysroots for compiling and linking.
// If we detect link flags, then we pass the link sysroot instead.
// FIXME: Use a more robust way to detect link line expansion.
- if (replaceValues.LinkFlags) {
+ if (this->ReplaceValues->LinkFlags) {
sysroot = this->LinkerSysroot;
} else {
sysroot = this->CompilerSysroot;
@@ -322,7 +322,7 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable(
if (!sysroot.empty() && !compilerOptionSysroot.empty()) {
ret += " ";
ret += compilerOptionSysroot;
- ret += outputConverter->EscapeForShell(sysroot, true);
+ ret += this->OutputConverter->EscapeForShell(sysroot, true);
}
return ret;
}
@@ -331,13 +331,13 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable(
if (mapIt != this->VariableMappings.end()) {
if (variable.find("_FLAG") == std::string::npos) {
std::string ret =
- outputConverter->ConvertToOutputForExisting(mapIt->second);
+ this->OutputConverter->ConvertToOutputForExisting(mapIt->second);
- if (replaceValues.Launcher && variable == "CMAKE_LINKER") {
+ if (this->ReplaceValues->Launcher && variable == "CMAKE_LINKER") {
// Add launcher as part of expansion so that it always appears
// immediately before the command itself, regardless of whether the
// overall rule template contains other content at the front.
- ret = cmStrCat(replaceValues.Launcher, " ", ret);
+ ret = cmStrCat(this->ReplaceValues->Launcher, " ", ret);
}
return ret;
@@ -351,47 +351,8 @@ void cmRulePlaceholderExpander::ExpandRuleVariables(
cmOutputConverter* outputConverter, std::string& s,
const RuleVariables& replaceValues)
{
- std::string::size_type start = s.find('<');
- // no variables to expand
- if (start == std::string::npos) {
- return;
- }
- std::string::size_type pos = 0;
- std::string expandedInput;
- while (start != std::string::npos && start < s.size() - 2) {
- std::string::size_type end = s.find('>', start);
- // if we find a < with no > we are done
- if (end == std::string::npos) {
- return;
- }
- char c = s[start + 1];
- // if the next char after the < is not A-Za-z then
- // skip it and try to find the next < in the string
- if (!isalpha(c)) {
- start = s.find('<', start + 1);
- } else {
- // extract the var
- std::string var = s.substr(start + 1, end - start - 1);
- std::string replace =
- this->ExpandRuleVariable(outputConverter, var, replaceValues);
- expandedInput += s.substr(pos, start - pos);
-
- // Prevent consecutive whitespace in the output if the rule variable
- // expands to an empty string.
- bool consecutive = replace.empty() && start > 0 && s[start - 1] == ' ' &&
- end + 1 < s.size() && s[end + 1] == ' ';
- if (consecutive) {
- expandedInput.pop_back();
- }
+ this->OutputConverter = outputConverter;
+ this->ReplaceValues = &replaceValues;
- expandedInput += replace;
-
- // move to next one
- start = s.find('<', start + var.size() + 2);
- pos = end + 1;
- }
- }
- // add the rest of the input
- expandedInput += s.substr(pos, s.size() - pos);
- s = expandedInput;
+ this->ExpandVariables(s);
}
diff --git a/Source/cmRulePlaceholderExpander.h b/Source/cmRulePlaceholderExpander.h
index 852954f..23ec405 100644
--- a/Source/cmRulePlaceholderExpander.h
+++ b/Source/cmRulePlaceholderExpander.h
@@ -8,9 +8,11 @@
#include <map>
#include <string>
+#include "cmPlaceholderExpander.h"
+
class cmOutputConverter;
-class cmRulePlaceholderExpander
+class cmRulePlaceholderExpander : public cmPlaceholderExpander
{
public:
cmRulePlaceholderExpander(
@@ -76,16 +78,16 @@ public:
std::string& string,
const RuleVariables& replaceValues);
- // Expand rule variables in a single string
- std::string ExpandRuleVariable(cmOutputConverter* outputConverter,
- std::string const& variable,
- const RuleVariables& replaceValues);
-
private:
+ std::string ExpandVariable(std::string const& variable) override;
+
std::string TargetImpLib;
std::map<std::string, std::string> Compilers;
std::map<std::string, std::string> VariableMappings;
std::string CompilerSysroot;
std::string LinkerSysroot;
+
+ cmOutputConverter* OutputConverter = nullptr;
+ RuleVariables const* ReplaceValues = nullptr;
};
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 6059055..92a0ac4 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -1783,69 +1783,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.";
- }
- context->IssueMessage(MessageType::FATAL_ERROR, e.str());
-}
-
-static void cmTargetCheckINTERFACE_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 " << 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());
}
- // Report an error.
- std::ostringstream e;
+ CheckLinkLibraryPattern(base, value, context);
+}
+
+void CheckLINK_LIBRARIES(const std::string& value, cmMakefile* context)
+{
+ CheckLinkLibraryPattern("LINK_LIBRARIES", value, context);
+}
- 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.";
+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)) {
+ // 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());
+ }
- 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 =
@@ -1861,6 +1885,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
@@ -1868,22 +1893,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);
}
}
}
diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx
index e15c941..0b96b2d 100644
--- a/Source/cmTargetLinkLibrariesCommand.cxx
+++ b/Source/cmTargetLinkLibrariesCommand.cxx