| /* 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" | 
 |  | 
 | 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.GetGlobalGenerator()->FindTarget(args[0]); | 
 |   if (!target) { | 
 |     for (const auto& importedTarget : mf.GetOwnedImportedTargets()) { | 
 |       if (importedTarget->GetName() == args[0]) { | 
 |         target = importedTarget.get(); | 
 |         break; | 
 |       } | 
 |     } | 
 |   } | 
 |   if (!target) { | 
 |     mf.IssueMessage(MessageType::FATAL_ERROR, | 
 |                     cmStrCat("Cannot specify link libraries for target \"", | 
 |                              args[0], | 
 |                              "\" which is not built by this project.")); | 
 |     cmSystemTools::SetFatalErrorOccurred(); | 
 |     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::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 consecutive 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(); | 
 |   } | 
 |  | 
 |   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::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::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)); | 
 |   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 |