| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file Copyright.txt or https://cmake.org/licensing for details. */ | |
| #include "cmTargetLinkLibrariesCommand.h" | |
| #include <cassert> | |
| #include <cstddef> | |
| #include <memory> | |
| #include <sstream> | |
| #include <unordered_set> | |
| #include <utility> | |
| #include <cm/optional> | |
| #include <cm/string_view> | |
| #include "cmExecutionStatus.h" | |
| #include "cmGeneratorExpression.h" | |
| #include "cmGlobalGenerator.h" | |
| #include "cmListFileCache.h" | |
| #include "cmMakefile.h" | |
| #include "cmMessageType.h" | |
| #include "cmPolicies.h" | |
| #include "cmState.h" | |
| #include "cmStateTypes.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmTarget.h" | |
| #include "cmTargetLinkLibraryType.h" | |
| #include "cmValue.h" | |
| #include "cmake.h" | |
| namespace { | |
| enum ProcessingState | |
| { | |
| ProcessingLinkLibraries, | |
| ProcessingPlainLinkInterface, | |
| ProcessingKeywordLinkInterface, | |
| ProcessingPlainPublicInterface, | |
| ProcessingKeywordPublicInterface, | |
| ProcessingPlainPrivateInterface, | |
| ProcessingKeywordPrivateInterface | |
| }; | |
| const char* LinkLibraryTypeNames[3] = { "general", "debug", "optimized" }; | |
| struct TLL | |
| { | |
| cmMakefile& Makefile; | |
| cmTarget* Target; | |
| bool WarnRemoteInterface = false; | |
| bool RejectRemoteLinking = false; | |
| bool EncodeRemoteReference = false; | |
| std::string DirectoryId; | |
| std::unordered_set<std::string> Props; | |
| TLL(cmMakefile& mf, cmTarget* target); | |
| ~TLL(); | |
| bool HandleLibrary(ProcessingState currentProcessingState, | |
| const std::string& lib, cmTargetLinkLibraryType llt); | |
| void AppendProperty(std::string const& prop, std::string const& value); | |
| void AffectsProperty(std::string const& prop); | |
| }; | |
| } // namespace | |
| static void LinkLibraryTypeSpecifierWarning(cmMakefile& mf, int left, | |
| int right); | |
| bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| // Must have at least one argument. | |
| if (args.empty()) { | |
| status.SetError("called with incorrect number of arguments"); | |
| return false; | |
| } | |
| cmMakefile& mf = status.GetMakefile(); | |
| // Alias targets cannot be on the LHS of this command. | |
| if (mf.IsAlias(args[0])) { | |
| status.SetError("can not be used on an ALIAS target."); | |
| return false; | |
| } | |
| // Lookup the target for which libraries are specified. | |
| cmTarget* target = | |
| mf.GetCMakeInstance()->GetGlobalGenerator()->FindTarget(args[0]); | |
| if (!target) { | |
| for (const auto& importedTarget : mf.GetOwnedImportedTargets()) { | |
| if (importedTarget->GetName() == args[0]) { | |
| target = importedTarget.get(); | |
| break; | |
| } | |
| } | |
| } | |
| if (!target) { | |
| MessageType t = MessageType::FATAL_ERROR; // fail by default | |
| std::ostringstream e; | |
| e << "Cannot specify link libraries for target \"" << args[0] << "\" " | |
| << "which is not built by this project."; | |
| // The bad target is the only argument. Check how policy CMP0016 is set, | |
| // and accept, warn or fail respectively: | |
| if (args.size() < 2) { | |
| switch (mf.GetPolicyStatus(cmPolicies::CMP0016)) { | |
| case cmPolicies::WARN: | |
| t = MessageType::AUTHOR_WARNING; | |
| // Print the warning. | |
| e << "\n" | |
| << "CMake does not support this but it used to work accidentally " | |
| << "and is being allowed for compatibility." | |
| << "\n" | |
| << cmPolicies::GetPolicyWarning(cmPolicies::CMP0016); | |
| break; | |
| case cmPolicies::OLD: // OLD behavior does not warn. | |
| t = MessageType::MESSAGE; | |
| break; | |
| case cmPolicies::REQUIRED_IF_USED: | |
| case cmPolicies::REQUIRED_ALWAYS: | |
| e << "\n" << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0016); | |
| break; | |
| case cmPolicies::NEW: // NEW behavior prints the error. | |
| break; | |
| } | |
| } | |
| // Now actually print the message. | |
| switch (t) { | |
| case MessageType::AUTHOR_WARNING: | |
| mf.IssueMessage(MessageType::AUTHOR_WARNING, e.str()); | |
| break; | |
| case MessageType::FATAL_ERROR: | |
| mf.IssueMessage(MessageType::FATAL_ERROR, e.str()); | |
| cmSystemTools::SetFatalErrorOccurred(); | |
| break; | |
| default: | |
| break; | |
| } | |
| return true; | |
| } | |
| // Having a UTILITY library on the LHS is a bug. | |
| if (target->GetType() == cmStateEnums::UTILITY) { | |
| std::ostringstream e; | |
| const char* modal = nullptr; | |
| MessageType messageType = MessageType::AUTHOR_WARNING; | |
| switch (mf.GetPolicyStatus(cmPolicies::CMP0039)) { | |
| case cmPolicies::WARN: | |
| e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0039) << "\n"; | |
| modal = "should"; | |
| CM_FALLTHROUGH; | |
| case cmPolicies::OLD: | |
| break; | |
| case cmPolicies::REQUIRED_ALWAYS: | |
| case cmPolicies::REQUIRED_IF_USED: | |
| case cmPolicies::NEW: | |
| modal = "must"; | |
| messageType = MessageType::FATAL_ERROR; | |
| break; | |
| } | |
| if (modal) { | |
| e << "Utility target \"" << target->GetName() << "\" " << modal | |
| << " not be used as the target of a target_link_libraries call."; | |
| mf.IssueMessage(messageType, e.str()); | |
| if (messageType == MessageType::FATAL_ERROR) { | |
| return false; | |
| } | |
| } | |
| } | |
| // But we might not have any libs after variable expansion. | |
| if (args.size() < 2) { | |
| return true; | |
| } | |
| TLL tll(mf, target); | |
| // Keep track of link configuration specifiers. | |
| cmTargetLinkLibraryType llt = GENERAL_LibraryType; | |
| bool haveLLT = false; | |
| // Start with primary linking and switch to link interface | |
| // specification if the keyword is encountered as the first argument. | |
| ProcessingState currentProcessingState = ProcessingLinkLibraries; | |
| // Accumulate consectuive non-keyword arguments into one entry in | |
| // order to handle unquoted generator expressions containing ';'. | |
| std::size_t genexNesting = 0; | |
| cm::optional<std::string> currentEntry; | |
| auto processCurrentEntry = [&]() -> bool { | |
| // FIXME: Warn about partial genex if genexNesting > 0? | |
| genexNesting = 0; | |
| if (currentEntry) { | |
| assert(!haveLLT); | |
| if (!tll.HandleLibrary(currentProcessingState, *currentEntry, | |
| GENERAL_LibraryType)) { | |
| return false; | |
| } | |
| currentEntry = cm::nullopt; | |
| } | |
| return true; | |
| }; | |
| auto extendCurrentEntry = [¤tEntry](std::string const& arg) { | |
| if (currentEntry) { | |
| currentEntry = cmStrCat(*currentEntry, ';', arg); | |
| } else { | |
| currentEntry = arg; | |
| } | |
| }; | |
| // Keep this list in sync with the keyword dispatch below. | |
| static std::unordered_set<std::string> const keywords{ | |
| "LINK_INTERFACE_LIBRARIES", | |
| "INTERFACE", | |
| "LINK_PUBLIC", | |
| "PUBLIC", | |
| "LINK_PRIVATE", | |
| "PRIVATE", | |
| "debug", | |
| "optimized", | |
| "general", | |
| }; | |
| // Add libraries, note that there is an optional prefix | |
| // of debug and optimized that can be used. | |
| for (unsigned int i = 1; i < args.size(); ++i) { | |
| if (keywords.count(args[i])) { | |
| // A keyword argument terminates any accumulated partial genex. | |
| if (!processCurrentEntry()) { | |
| return false; | |
| } | |
| // Process this keyword argument. | |
| if (args[i] == "LINK_INTERFACE_LIBRARIES") { | |
| currentProcessingState = ProcessingPlainLinkInterface; | |
| if (i != 1) { | |
| mf.IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| "The LINK_INTERFACE_LIBRARIES option must appear as the " | |
| "second argument, just after the target name."); | |
| return true; | |
| } | |
| } else if (args[i] == "INTERFACE") { | |
| if (i != 1 && | |
| currentProcessingState != ProcessingKeywordPrivateInterface && | |
| currentProcessingState != ProcessingKeywordPublicInterface && | |
| currentProcessingState != ProcessingKeywordLinkInterface) { | |
| mf.IssueMessage(MessageType::FATAL_ERROR, | |
| "The INTERFACE, PUBLIC or PRIVATE option must " | |
| "appear as the second argument, just after the " | |
| "target name."); | |
| return true; | |
| } | |
| currentProcessingState = ProcessingKeywordLinkInterface; | |
| } else if (args[i] == "LINK_PUBLIC") { | |
| if (i != 1 && | |
| currentProcessingState != ProcessingPlainPrivateInterface && | |
| currentProcessingState != ProcessingPlainPublicInterface) { | |
| mf.IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| "The LINK_PUBLIC or LINK_PRIVATE option must appear as the " | |
| "second argument, just after the target name."); | |
| return true; | |
| } | |
| currentProcessingState = ProcessingPlainPublicInterface; | |
| } else if (args[i] == "PUBLIC") { | |
| if (i != 1 && | |
| currentProcessingState != ProcessingKeywordPrivateInterface && | |
| currentProcessingState != ProcessingKeywordPublicInterface && | |
| currentProcessingState != ProcessingKeywordLinkInterface) { | |
| mf.IssueMessage(MessageType::FATAL_ERROR, | |
| "The INTERFACE, PUBLIC or PRIVATE option must " | |
| "appear as the second argument, just after the " | |
| "target name."); | |
| return true; | |
| } | |
| currentProcessingState = ProcessingKeywordPublicInterface; | |
| } else if (args[i] == "LINK_PRIVATE") { | |
| if (i != 1 && | |
| currentProcessingState != ProcessingPlainPublicInterface && | |
| currentProcessingState != ProcessingPlainPrivateInterface) { | |
| mf.IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| "The LINK_PUBLIC or LINK_PRIVATE option must appear as the " | |
| "second argument, just after the target name."); | |
| return true; | |
| } | |
| currentProcessingState = ProcessingPlainPrivateInterface; | |
| } else if (args[i] == "PRIVATE") { | |
| if (i != 1 && | |
| currentProcessingState != ProcessingKeywordPrivateInterface && | |
| currentProcessingState != ProcessingKeywordPublicInterface && | |
| currentProcessingState != ProcessingKeywordLinkInterface) { | |
| mf.IssueMessage(MessageType::FATAL_ERROR, | |
| "The INTERFACE, PUBLIC or PRIVATE option must " | |
| "appear as the second argument, just after the " | |
| "target name."); | |
| return true; | |
| } | |
| currentProcessingState = ProcessingKeywordPrivateInterface; | |
| } else if (args[i] == "debug") { | |
| if (haveLLT) { | |
| LinkLibraryTypeSpecifierWarning(mf, llt, DEBUG_LibraryType); | |
| } | |
| llt = DEBUG_LibraryType; | |
| haveLLT = true; | |
| } else if (args[i] == "optimized") { | |
| if (haveLLT) { | |
| LinkLibraryTypeSpecifierWarning(mf, llt, OPTIMIZED_LibraryType); | |
| } | |
| llt = OPTIMIZED_LibraryType; | |
| haveLLT = true; | |
| } else if (args[i] == "general") { | |
| if (haveLLT) { | |
| LinkLibraryTypeSpecifierWarning(mf, llt, GENERAL_LibraryType); | |
| } | |
| llt = GENERAL_LibraryType; | |
| haveLLT = true; | |
| } | |
| } else if (haveLLT) { | |
| // The link type was specified by the previous argument. | |
| haveLLT = false; | |
| assert(!currentEntry); | |
| if (!tll.HandleLibrary(currentProcessingState, args[i], llt)) { | |
| return false; | |
| } | |
| llt = GENERAL_LibraryType; | |
| } else { | |
| // Track the genex nesting level. | |
| { | |
| cm::string_view arg = args[i]; | |
| for (std::string::size_type pos = 0; pos < arg.size(); ++pos) { | |
| cm::string_view cur = arg.substr(pos); | |
| if (cmHasLiteralPrefix(cur, "$<")) { | |
| ++genexNesting; | |
| ++pos; | |
| } else if (genexNesting > 0 && cmHasLiteralPrefix(cur, ">")) { | |
| --genexNesting; | |
| } | |
| } | |
| } | |
| // Accumulate this argument in the current entry. | |
| extendCurrentEntry(args[i]); | |
| // Process this entry if it does not end inside a genex. | |
| if (genexNesting == 0) { | |
| if (!processCurrentEntry()) { | |
| return false; | |
| } | |
| } | |
| } | |
| } | |
| // Process the last accumulated partial genex, if any. | |
| if (!processCurrentEntry()) { | |
| return false; | |
| } | |
| // Make sure the last argument was not a library type specifier. | |
| if (haveLLT) { | |
| mf.IssueMessage(MessageType::FATAL_ERROR, | |
| cmStrCat("The \"", LinkLibraryTypeNames[llt], | |
| "\" argument must be followed by a library.")); | |
| cmSystemTools::SetFatalErrorOccurred(); | |
| } | |
| const cmPolicies::PolicyStatus policy22Status = | |
| target->GetPolicyStatusCMP0022(); | |
| // If any of the LINK_ options were given, make sure the | |
| // LINK_INTERFACE_LIBRARIES target property exists. | |
| // Use of any of the new keywords implies awareness of | |
| // this property. And if no libraries are named, it should | |
| // result in an empty link interface. | |
| if ((policy22Status == cmPolicies::OLD || | |
| policy22Status == cmPolicies::WARN) && | |
| currentProcessingState != ProcessingLinkLibraries && | |
| !target->GetProperty("LINK_INTERFACE_LIBRARIES")) { | |
| target->SetProperty("LINK_INTERFACE_LIBRARIES", ""); | |
| } | |
| return true; | |
| } | |
| static void LinkLibraryTypeSpecifierWarning(cmMakefile& mf, int left, | |
| int right) | |
| { | |
| mf.IssueMessage( | |
| MessageType::AUTHOR_WARNING, | |
| cmStrCat( | |
| "Link library type specifier \"", LinkLibraryTypeNames[left], | |
| "\" is followed by specifier \"", LinkLibraryTypeNames[right], | |
| "\" instead of a library name. The first specifier will be ignored.")); | |
| } | |
| namespace { | |
| TLL::TLL(cmMakefile& mf, cmTarget* target) | |
| : Makefile(mf) | |
| , Target(target) | |
| { | |
| if (&this->Makefile != this->Target->GetMakefile()) { | |
| // The LHS target was created in another directory. | |
| switch (this->Makefile.GetPolicyStatus(cmPolicies::CMP0079)) { | |
| case cmPolicies::WARN: | |
| this->WarnRemoteInterface = true; | |
| CM_FALLTHROUGH; | |
| case cmPolicies::OLD: | |
| this->RejectRemoteLinking = true; | |
| break; | |
| case cmPolicies::REQUIRED_ALWAYS: | |
| case cmPolicies::REQUIRED_IF_USED: | |
| case cmPolicies::NEW: | |
| this->EncodeRemoteReference = true; | |
| break; | |
| } | |
| } | |
| if (this->EncodeRemoteReference) { | |
| cmDirectoryId const dirId = this->Makefile.GetDirectoryId(); | |
| this->DirectoryId = cmStrCat(CMAKE_DIRECTORY_ID_SEP, dirId.String); | |
| } | |
| } | |
| bool TLL::HandleLibrary(ProcessingState currentProcessingState, | |
| const std::string& lib, cmTargetLinkLibraryType llt) | |
| { | |
| if (this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY && | |
| currentProcessingState != ProcessingKeywordLinkInterface) { | |
| this->Makefile.IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| "INTERFACE library can only be used with the INTERFACE keyword of " | |
| "target_link_libraries"); | |
| return false; | |
| } | |
| if (this->Target->IsImported() && | |
| currentProcessingState != ProcessingKeywordLinkInterface) { | |
| this->Makefile.IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| "IMPORTED library can only be used with the INTERFACE keyword of " | |
| "target_link_libraries"); | |
| return false; | |
| } | |
| cmTarget::TLLSignature sig = | |
| (currentProcessingState == ProcessingPlainPrivateInterface || | |
| currentProcessingState == ProcessingPlainPublicInterface || | |
| currentProcessingState == ProcessingKeywordPrivateInterface || | |
| currentProcessingState == ProcessingKeywordPublicInterface || | |
| currentProcessingState == ProcessingKeywordLinkInterface) | |
| ? cmTarget::KeywordTLLSignature | |
| : cmTarget::PlainTLLSignature; | |
| if (!this->Target->PushTLLCommandTrace( | |
| sig, this->Makefile.GetBacktrace().Top())) { | |
| std::ostringstream e; | |
| const char* modal = nullptr; | |
| MessageType messageType = MessageType::AUTHOR_WARNING; | |
| switch (this->Makefile.GetPolicyStatus(cmPolicies::CMP0023)) { | |
| case cmPolicies::WARN: | |
| e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0023) << "\n"; | |
| modal = "should"; | |
| CM_FALLTHROUGH; | |
| case cmPolicies::OLD: | |
| break; | |
| case cmPolicies::REQUIRED_ALWAYS: | |
| case cmPolicies::REQUIRED_IF_USED: | |
| case cmPolicies::NEW: | |
| modal = "must"; | |
| messageType = MessageType::FATAL_ERROR; | |
| break; | |
| } | |
| if (modal) { | |
| // If the sig is a keyword form and there is a conflict, the existing | |
| // form must be the plain form. | |
| const char* existingSig = | |
| (sig == cmTarget::KeywordTLLSignature ? "plain" : "keyword"); | |
| e << "The " << existingSig | |
| << " signature for target_link_libraries has " | |
| "already been used with the target \"" | |
| << this->Target->GetName() | |
| << "\". All uses of target_link_libraries with a target " << modal | |
| << " be either all-keyword or all-plain.\n"; | |
| this->Target->GetTllSignatureTraces(e, | |
| sig == cmTarget::KeywordTLLSignature | |
| ? cmTarget::PlainTLLSignature | |
| : cmTarget::KeywordTLLSignature); | |
| this->Makefile.IssueMessage(messageType, e.str()); | |
| if (messageType == MessageType::FATAL_ERROR) { | |
| return false; | |
| } | |
| } | |
| } | |
| // Handle normal case where the command was called with another keyword than | |
| // INTERFACE / LINK_INTERFACE_LIBRARIES or none at all. (The "LINK_LIBRARIES" | |
| // property of the target on the LHS shall be populated.) | |
| if (currentProcessingState != ProcessingKeywordLinkInterface && | |
| currentProcessingState != ProcessingPlainLinkInterface) { | |
| if (this->RejectRemoteLinking) { | |
| this->Makefile.IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Attempt to add link library \"", lib, "\" to target \"", | |
| this->Target->GetName(), | |
| "\" which is not built in this " | |
| "directory.\nThis is allowed only when policy CMP0079 " | |
| "is set to NEW.")); | |
| return false; | |
| } | |
| cmTarget* tgt = this->Makefile.GetGlobalGenerator()->FindTarget(lib); | |
| if (tgt && (tgt->GetType() != cmStateEnums::STATIC_LIBRARY) && | |
| (tgt->GetType() != cmStateEnums::SHARED_LIBRARY) && | |
| (tgt->GetType() != cmStateEnums::UNKNOWN_LIBRARY) && | |
| (tgt->GetType() != cmStateEnums::OBJECT_LIBRARY) && | |
| (tgt->GetType() != cmStateEnums::INTERFACE_LIBRARY) && | |
| !tgt->IsExecutableWithExports()) { | |
| this->Makefile.IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat( | |
| "Target \"", lib, "\" of type ", | |
| cmState::GetTargetTypeName(tgt->GetType()), | |
| " may not be linked into another target. One may link only to " | |
| "INTERFACE, OBJECT, STATIC or SHARED libraries, or to ", | |
| "executables with the ENABLE_EXPORTS property set.")); | |
| } | |
| this->AffectsProperty("LINK_LIBRARIES"); | |
| this->Target->AddLinkLibrary(this->Makefile, lib, llt); | |
| } | |
| if (this->WarnRemoteInterface) { | |
| this->Makefile.IssueMessage( | |
| MessageType::AUTHOR_WARNING, | |
| cmStrCat( | |
| cmPolicies::GetPolicyWarning(cmPolicies::CMP0079), "\nTarget\n ", | |
| this->Target->GetName(), | |
| "\nis not created in this " | |
| "directory. For compatibility with older versions of CMake, link " | |
| "library\n ", | |
| lib, | |
| "\nwill be looked up in the directory in which " | |
| "the target was created rather than in this calling directory.")); | |
| } | |
| // Handle (additional) case where the command was called with PRIVATE / | |
| // LINK_PRIVATE and stop its processing. (The "INTERFACE_LINK_LIBRARIES" | |
| // property of the target on the LHS shall only be populated if it is a | |
| // STATIC library.) | |
| if (currentProcessingState == ProcessingKeywordPrivateInterface || | |
| currentProcessingState == ProcessingPlainPrivateInterface) { | |
| if (this->Target->GetType() == cmStateEnums::STATIC_LIBRARY || | |
| this->Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { | |
| // TODO: Detect and no-op `$<COMPILE_ONLY>` genexes here. | |
| std::string configLib = | |
| this->Target->GetDebugGeneratorExpressions(lib, llt); | |
| if (cmGeneratorExpression::IsValidTargetName(lib) || | |
| cmGeneratorExpression::Find(lib) != std::string::npos) { | |
| configLib = "$<LINK_ONLY:" + configLib + ">"; | |
| } | |
| this->AppendProperty("INTERFACE_LINK_LIBRARIES", configLib); | |
| } | |
| return true; | |
| } | |
| // Handle general case where the command was called with another keyword than | |
| // PRIVATE / LINK_PRIVATE or none at all. (The "INTERFACE_LINK_LIBRARIES" | |
| // property of the target on the LHS shall be populated.) | |
| this->AppendProperty("INTERFACE_LINK_LIBRARIES", | |
| this->Target->GetDebugGeneratorExpressions(lib, llt)); | |
| // Stop processing if called without any keyword. | |
| if (currentProcessingState == ProcessingLinkLibraries) { | |
| return true; | |
| } | |
| // Stop processing if policy CMP0022 is set to NEW. | |
| const cmPolicies::PolicyStatus policy22Status = | |
| this->Target->GetPolicyStatusCMP0022(); | |
| if (policy22Status != cmPolicies::OLD && | |
| policy22Status != cmPolicies::WARN) { | |
| return true; | |
| } | |
| // Stop processing if called with an INTERFACE library on the LHS. | |
| if (this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { | |
| return true; | |
| } | |
| // Handle (additional) backward-compatibility case where the command was | |
| // called with PUBLIC / INTERFACE / LINK_PUBLIC / LINK_INTERFACE_LIBRARIES. | |
| // (The policy CMP0022 is not set to NEW.) | |
| { | |
| // Get the list of configurations considered to be DEBUG. | |
| std::vector<std::string> debugConfigs = | |
| this->Makefile.GetCMakeInstance()->GetDebugConfigs(); | |
| std::string prop; | |
| // Include this library in the link interface for the target. | |
| if (llt == DEBUG_LibraryType || llt == GENERAL_LibraryType) { | |
| // Put in the DEBUG configuration interfaces. | |
| for (std::string const& dc : debugConfigs) { | |
| prop = cmStrCat("LINK_INTERFACE_LIBRARIES_", dc); | |
| this->AppendProperty(prop, lib); | |
| } | |
| } | |
| if (llt == OPTIMIZED_LibraryType || llt == GENERAL_LibraryType) { | |
| // Put in the non-DEBUG configuration interfaces. | |
| this->AppendProperty("LINK_INTERFACE_LIBRARIES", lib); | |
| // Make sure the DEBUG configuration interfaces exist so that the | |
| // general one will not be used as a fall-back. | |
| for (std::string const& dc : debugConfigs) { | |
| prop = cmStrCat("LINK_INTERFACE_LIBRARIES_", dc); | |
| if (!this->Target->GetProperty(prop)) { | |
| this->Target->SetProperty(prop, ""); | |
| } | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| void TLL::AppendProperty(std::string const& prop, std::string const& value) | |
| { | |
| this->AffectsProperty(prop); | |
| this->Target->AppendProperty(prop, value, this->Makefile.GetBacktrace()); | |
| } | |
| void TLL::AffectsProperty(std::string const& prop) | |
| { | |
| if (!this->EncodeRemoteReference) { | |
| return; | |
| } | |
| // Add a wrapper to the expression to tell LookupLinkItem to look up | |
| // names in the caller's directory. | |
| if (this->Props.insert(prop).second) { | |
| this->Target->AppendProperty(prop, this->DirectoryId, | |
| this->Makefile.GetBacktrace()); | |
| } | |
| } | |
| TLL::~TLL() | |
| { | |
| for (std::string const& prop : this->Props) { | |
| this->Target->AppendProperty(prop, CMAKE_DIRECTORY_ID_SEP, | |
| this->Makefile.GetBacktrace()); | |
| } | |
| } | |
| } // namespace |