1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmTargetLinkLibrariesCommand.h"
8 #include <unordered_set>
11 #include <cm/optional>
12 #include <cm/string_view>
14 #include "cmExecutionStatus.h"
15 #include "cmGeneratorExpression.h"
16 #include "cmGlobalGenerator.h"
17 #include "cmListFileCache.h"
18 #include "cmMakefile.h"
19 #include "cmMessageType.h"
20 #include "cmPolicies.h"
22 #include "cmStateTypes.h"
23 #include "cmStringAlgorithms.h"
24 #include "cmSystemTools.h"
26 #include "cmTargetLinkLibraryType.h"
34 ProcessingLinkLibraries,
35 ProcessingPlainLinkInterface,
36 ProcessingKeywordLinkInterface,
37 ProcessingPlainPublicInterface,
38 ProcessingKeywordPublicInterface,
39 ProcessingPlainPrivateInterface,
40 ProcessingKeywordPrivateInterface
43 const char* LinkLibraryTypeNames[3] = { "general", "debug", "optimized" };
49 bool WarnRemoteInterface = false;
50 bool RejectRemoteLinking = false;
51 bool EncodeRemoteReference = false;
52 std::string DirectoryId;
53 std::unordered_set<std::string> Props;
55 TLL(cmMakefile& mf, cmTarget* target);
58 bool HandleLibrary(ProcessingState currentProcessingState,
59 const std::string& lib, cmTargetLinkLibraryType llt);
60 void AppendProperty(std::string const& prop, std::string const& value);
61 void AffectsProperty(std::string const& prop);
66 static void LinkLibraryTypeSpecifierWarning(cmMakefile& mf, int left,
69 bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args,
70 cmExecutionStatus& status)
72 // Must have at least one argument.
74 status.SetError("called with incorrect number of arguments");
78 cmMakefile& mf = status.GetMakefile();
80 // Alias targets cannot be on the LHS of this command.
81 if (mf.IsAlias(args[0])) {
82 status.SetError("can not be used on an ALIAS target.");
86 // Lookup the target for which libraries are specified.
88 mf.GetCMakeInstance()->GetGlobalGenerator()->FindTarget(args[0]);
90 for (const auto& importedTarget : mf.GetOwnedImportedTargets()) {
91 if (importedTarget->GetName() == args[0]) {
92 target = importedTarget.get();
98 MessageType t = MessageType::FATAL_ERROR; // fail by default
100 e << "Cannot specify link libraries for target \"" << args[0] << "\" "
101 << "which is not built by this project.";
102 // The bad target is the only argument. Check how policy CMP0016 is set,
103 // and accept, warn or fail respectively:
104 if (args.size() < 2) {
105 switch (mf.GetPolicyStatus(cmPolicies::CMP0016)) {
106 case cmPolicies::WARN:
107 t = MessageType::AUTHOR_WARNING;
108 // Print the warning.
110 << "CMake does not support this but it used to work accidentally "
111 << "and is being allowed for compatibility."
113 << cmPolicies::GetPolicyWarning(cmPolicies::CMP0016);
115 case cmPolicies::OLD: // OLD behavior does not warn.
116 t = MessageType::MESSAGE;
118 case cmPolicies::REQUIRED_IF_USED:
119 case cmPolicies::REQUIRED_ALWAYS:
120 e << "\n" << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0016);
122 case cmPolicies::NEW: // NEW behavior prints the error.
126 // Now actually print the message.
128 case MessageType::AUTHOR_WARNING:
129 mf.IssueMessage(MessageType::AUTHOR_WARNING, e.str());
131 case MessageType::FATAL_ERROR:
132 mf.IssueMessage(MessageType::FATAL_ERROR, e.str());
133 cmSystemTools::SetFatalErrorOccurred();
141 // Having a UTILITY library on the LHS is a bug.
142 if (target->GetType() == cmStateEnums::UTILITY) {
143 std::ostringstream e;
144 const char* modal = nullptr;
145 MessageType messageType = MessageType::AUTHOR_WARNING;
146 switch (mf.GetPolicyStatus(cmPolicies::CMP0039)) {
147 case cmPolicies::WARN:
148 e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0039) << "\n";
151 case cmPolicies::OLD:
153 case cmPolicies::REQUIRED_ALWAYS:
154 case cmPolicies::REQUIRED_IF_USED:
155 case cmPolicies::NEW:
157 messageType = MessageType::FATAL_ERROR;
161 e << "Utility target \"" << target->GetName() << "\" " << modal
162 << " not be used as the target of a target_link_libraries call.";
163 mf.IssueMessage(messageType, e.str());
164 if (messageType == MessageType::FATAL_ERROR) {
170 // But we might not have any libs after variable expansion.
171 if (args.size() < 2) {
177 // Keep track of link configuration specifiers.
178 cmTargetLinkLibraryType llt = GENERAL_LibraryType;
179 bool haveLLT = false;
181 // Start with primary linking and switch to link interface
182 // specification if the keyword is encountered as the first argument.
183 ProcessingState currentProcessingState = ProcessingLinkLibraries;
185 // Accumulate consectuive non-keyword arguments into one entry in
186 // order to handle unquoted generator expressions containing ';'.
187 std::size_t genexNesting = 0;
188 cm::optional<std::string> currentEntry;
189 auto processCurrentEntry = [&]() -> bool {
190 // FIXME: Warn about partial genex if genexNesting > 0?
194 if (!tll.HandleLibrary(currentProcessingState, *currentEntry,
195 GENERAL_LibraryType)) {
198 currentEntry = cm::nullopt;
202 auto extendCurrentEntry = [¤tEntry](std::string const& arg) {
204 currentEntry = cmStrCat(*currentEntry, ';', arg);
210 // Keep this list in sync with the keyword dispatch below.
211 static std::unordered_set<std::string> const keywords{
212 "LINK_INTERFACE_LIBRARIES",
223 // Add libraries, note that there is an optional prefix
224 // of debug and optimized that can be used.
225 for (unsigned int i = 1; i < args.size(); ++i) {
226 if (keywords.count(args[i])) {
227 // A keyword argument terminates any accumulated partial genex.
228 if (!processCurrentEntry()) {
232 // Process this keyword argument.
233 if (args[i] == "LINK_INTERFACE_LIBRARIES") {
234 currentProcessingState = ProcessingPlainLinkInterface;
237 MessageType::FATAL_ERROR,
238 "The LINK_INTERFACE_LIBRARIES option must appear as the "
239 "second argument, just after the target name.");
242 } else if (args[i] == "INTERFACE") {
244 currentProcessingState != ProcessingKeywordPrivateInterface &&
245 currentProcessingState != ProcessingKeywordPublicInterface &&
246 currentProcessingState != ProcessingKeywordLinkInterface) {
247 mf.IssueMessage(MessageType::FATAL_ERROR,
248 "The INTERFACE, PUBLIC or PRIVATE option must "
249 "appear as the second argument, just after the "
253 currentProcessingState = ProcessingKeywordLinkInterface;
254 } else if (args[i] == "LINK_PUBLIC") {
256 currentProcessingState != ProcessingPlainPrivateInterface &&
257 currentProcessingState != ProcessingPlainPublicInterface) {
259 MessageType::FATAL_ERROR,
260 "The LINK_PUBLIC or LINK_PRIVATE option must appear as the "
261 "second argument, just after the target name.");
264 currentProcessingState = ProcessingPlainPublicInterface;
265 } else if (args[i] == "PUBLIC") {
267 currentProcessingState != ProcessingKeywordPrivateInterface &&
268 currentProcessingState != ProcessingKeywordPublicInterface &&
269 currentProcessingState != ProcessingKeywordLinkInterface) {
270 mf.IssueMessage(MessageType::FATAL_ERROR,
271 "The INTERFACE, PUBLIC or PRIVATE option must "
272 "appear as the second argument, just after the "
276 currentProcessingState = ProcessingKeywordPublicInterface;
277 } else if (args[i] == "LINK_PRIVATE") {
279 currentProcessingState != ProcessingPlainPublicInterface &&
280 currentProcessingState != ProcessingPlainPrivateInterface) {
282 MessageType::FATAL_ERROR,
283 "The LINK_PUBLIC or LINK_PRIVATE option must appear as the "
284 "second argument, just after the target name.");
287 currentProcessingState = ProcessingPlainPrivateInterface;
288 } else if (args[i] == "PRIVATE") {
290 currentProcessingState != ProcessingKeywordPrivateInterface &&
291 currentProcessingState != ProcessingKeywordPublicInterface &&
292 currentProcessingState != ProcessingKeywordLinkInterface) {
293 mf.IssueMessage(MessageType::FATAL_ERROR,
294 "The INTERFACE, PUBLIC or PRIVATE option must "
295 "appear as the second argument, just after the "
299 currentProcessingState = ProcessingKeywordPrivateInterface;
300 } else if (args[i] == "debug") {
302 LinkLibraryTypeSpecifierWarning(mf, llt, DEBUG_LibraryType);
304 llt = DEBUG_LibraryType;
306 } else if (args[i] == "optimized") {
308 LinkLibraryTypeSpecifierWarning(mf, llt, OPTIMIZED_LibraryType);
310 llt = OPTIMIZED_LibraryType;
312 } else if (args[i] == "general") {
314 LinkLibraryTypeSpecifierWarning(mf, llt, GENERAL_LibraryType);
316 llt = GENERAL_LibraryType;
319 } else if (haveLLT) {
320 // The link type was specified by the previous argument.
322 assert(!currentEntry);
323 if (!tll.HandleLibrary(currentProcessingState, args[i], llt)) {
326 llt = GENERAL_LibraryType;
328 // Track the genex nesting level.
330 cm::string_view arg = args[i];
331 for (std::string::size_type pos = 0; pos < arg.size(); ++pos) {
332 cm::string_view cur = arg.substr(pos);
333 if (cmHasLiteralPrefix(cur, "$<")) {
336 } else if (genexNesting > 0 && cmHasLiteralPrefix(cur, ">")) {
342 // Accumulate this argument in the current entry.
343 extendCurrentEntry(args[i]);
345 // Process this entry if it does not end inside a genex.
346 if (genexNesting == 0) {
347 if (!processCurrentEntry()) {
354 // Process the last accumulated partial genex, if any.
355 if (!processCurrentEntry()) {
359 // Make sure the last argument was not a library type specifier.
361 mf.IssueMessage(MessageType::FATAL_ERROR,
362 cmStrCat("The \"", LinkLibraryTypeNames[llt],
363 "\" argument must be followed by a library."));
364 cmSystemTools::SetFatalErrorOccurred();
367 const cmPolicies::PolicyStatus policy22Status =
368 target->GetPolicyStatusCMP0022();
370 // If any of the LINK_ options were given, make sure the
371 // LINK_INTERFACE_LIBRARIES target property exists.
372 // Use of any of the new keywords implies awareness of
373 // this property. And if no libraries are named, it should
374 // result in an empty link interface.
375 if ((policy22Status == cmPolicies::OLD ||
376 policy22Status == cmPolicies::WARN) &&
377 currentProcessingState != ProcessingLinkLibraries &&
378 !target->GetProperty("LINK_INTERFACE_LIBRARIES")) {
379 target->SetProperty("LINK_INTERFACE_LIBRARIES", "");
385 static void LinkLibraryTypeSpecifierWarning(cmMakefile& mf, int left,
389 MessageType::AUTHOR_WARNING,
391 "Link library type specifier \"", LinkLibraryTypeNames[left],
392 "\" is followed by specifier \"", LinkLibraryTypeNames[right],
393 "\" instead of a library name. The first specifier will be ignored."));
398 TLL::TLL(cmMakefile& mf, cmTarget* target)
402 if (&this->Makefile != this->Target->GetMakefile()) {
403 // The LHS target was created in another directory.
404 switch (this->Makefile.GetPolicyStatus(cmPolicies::CMP0079)) {
405 case cmPolicies::WARN:
406 this->WarnRemoteInterface = true;
408 case cmPolicies::OLD:
409 this->RejectRemoteLinking = true;
411 case cmPolicies::REQUIRED_ALWAYS:
412 case cmPolicies::REQUIRED_IF_USED:
413 case cmPolicies::NEW:
414 this->EncodeRemoteReference = true;
418 if (this->EncodeRemoteReference) {
419 cmDirectoryId const dirId = this->Makefile.GetDirectoryId();
420 this->DirectoryId = cmStrCat(CMAKE_DIRECTORY_ID_SEP, dirId.String);
424 bool TLL::HandleLibrary(ProcessingState currentProcessingState,
425 const std::string& lib, cmTargetLinkLibraryType llt)
427 if (this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY &&
428 currentProcessingState != ProcessingKeywordLinkInterface) {
429 this->Makefile.IssueMessage(
430 MessageType::FATAL_ERROR,
431 "INTERFACE library can only be used with the INTERFACE keyword of "
432 "target_link_libraries");
435 if (this->Target->IsImported() &&
436 currentProcessingState != ProcessingKeywordLinkInterface) {
437 this->Makefile.IssueMessage(
438 MessageType::FATAL_ERROR,
439 "IMPORTED library can only be used with the INTERFACE keyword of "
440 "target_link_libraries");
444 cmTarget::TLLSignature sig =
445 (currentProcessingState == ProcessingPlainPrivateInterface ||
446 currentProcessingState == ProcessingPlainPublicInterface ||
447 currentProcessingState == ProcessingKeywordPrivateInterface ||
448 currentProcessingState == ProcessingKeywordPublicInterface ||
449 currentProcessingState == ProcessingKeywordLinkInterface)
450 ? cmTarget::KeywordTLLSignature
451 : cmTarget::PlainTLLSignature;
452 if (!this->Target->PushTLLCommandTrace(
453 sig, this->Makefile.GetBacktrace().Top())) {
454 std::ostringstream e;
455 const char* modal = nullptr;
456 MessageType messageType = MessageType::AUTHOR_WARNING;
457 switch (this->Makefile.GetPolicyStatus(cmPolicies::CMP0023)) {
458 case cmPolicies::WARN:
459 e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0023) << "\n";
462 case cmPolicies::OLD:
464 case cmPolicies::REQUIRED_ALWAYS:
465 case cmPolicies::REQUIRED_IF_USED:
466 case cmPolicies::NEW:
468 messageType = MessageType::FATAL_ERROR;
473 // If the sig is a keyword form and there is a conflict, the existing
474 // form must be the plain form.
475 const char* existingSig =
476 (sig == cmTarget::KeywordTLLSignature ? "plain" : "keyword");
477 e << "The " << existingSig
478 << " signature for target_link_libraries has "
479 "already been used with the target \""
480 << this->Target->GetName()
481 << "\". All uses of target_link_libraries with a target " << modal
482 << " be either all-keyword or all-plain.\n";
483 this->Target->GetTllSignatureTraces(e,
484 sig == cmTarget::KeywordTLLSignature
485 ? cmTarget::PlainTLLSignature
486 : cmTarget::KeywordTLLSignature);
487 this->Makefile.IssueMessage(messageType, e.str());
488 if (messageType == MessageType::FATAL_ERROR) {
494 // Handle normal case where the command was called with another keyword than
495 // INTERFACE / LINK_INTERFACE_LIBRARIES or none at all. (The "LINK_LIBRARIES"
496 // property of the target on the LHS shall be populated.)
497 if (currentProcessingState != ProcessingKeywordLinkInterface &&
498 currentProcessingState != ProcessingPlainLinkInterface) {
500 if (this->RejectRemoteLinking) {
501 this->Makefile.IssueMessage(
502 MessageType::FATAL_ERROR,
503 cmStrCat("Attempt to add link library \"", lib, "\" to target \"",
504 this->Target->GetName(),
505 "\" which is not built in this "
506 "directory.\nThis is allowed only when policy CMP0079 "
511 cmTarget* tgt = this->Makefile.GetGlobalGenerator()->FindTarget(lib);
513 if (tgt && (tgt->GetType() != cmStateEnums::STATIC_LIBRARY) &&
514 (tgt->GetType() != cmStateEnums::SHARED_LIBRARY) &&
515 (tgt->GetType() != cmStateEnums::UNKNOWN_LIBRARY) &&
516 (tgt->GetType() != cmStateEnums::OBJECT_LIBRARY) &&
517 (tgt->GetType() != cmStateEnums::INTERFACE_LIBRARY) &&
518 !tgt->IsExecutableWithExports()) {
519 this->Makefile.IssueMessage(
520 MessageType::FATAL_ERROR,
522 "Target \"", lib, "\" of type ",
523 cmState::GetTargetTypeName(tgt->GetType()),
524 " may not be linked into another target. One may link only to "
525 "INTERFACE, OBJECT, STATIC or SHARED libraries, or to ",
526 "executables with the ENABLE_EXPORTS property set."));
529 this->AffectsProperty("LINK_LIBRARIES");
530 this->Target->AddLinkLibrary(this->Makefile, lib, llt);
533 if (this->WarnRemoteInterface) {
534 this->Makefile.IssueMessage(
535 MessageType::AUTHOR_WARNING,
537 cmPolicies::GetPolicyWarning(cmPolicies::CMP0079), "\nTarget\n ",
538 this->Target->GetName(),
539 "\nis not created in this "
540 "directory. For compatibility with older versions of CMake, link "
543 "\nwill be looked up in the directory in which "
544 "the target was created rather than in this calling directory."));
547 // Handle (additional) case where the command was called with PRIVATE /
548 // LINK_PRIVATE and stop its processing. (The "INTERFACE_LINK_LIBRARIES"
549 // property of the target on the LHS shall only be populated if it is a
551 if (currentProcessingState == ProcessingKeywordPrivateInterface ||
552 currentProcessingState == ProcessingPlainPrivateInterface) {
553 if (this->Target->GetType() == cmStateEnums::STATIC_LIBRARY ||
554 this->Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
555 std::string configLib =
556 this->Target->GetDebugGeneratorExpressions(lib, llt);
557 if (cmGeneratorExpression::IsValidTargetName(lib) ||
558 cmGeneratorExpression::Find(lib) != std::string::npos) {
559 configLib = "$<LINK_ONLY:" + configLib + ">";
561 this->AppendProperty("INTERFACE_LINK_LIBRARIES", configLib);
566 // Handle general case where the command was called with another keyword than
567 // PRIVATE / LINK_PRIVATE or none at all. (The "INTERFACE_LINK_LIBRARIES"
568 // property of the target on the LHS shall be populated.)
569 this->AppendProperty("INTERFACE_LINK_LIBRARIES",
570 this->Target->GetDebugGeneratorExpressions(lib, llt));
572 // Stop processing if called without any keyword.
573 if (currentProcessingState == ProcessingLinkLibraries) {
576 // Stop processing if policy CMP0022 is set to NEW.
577 const cmPolicies::PolicyStatus policy22Status =
578 this->Target->GetPolicyStatusCMP0022();
579 if (policy22Status != cmPolicies::OLD &&
580 policy22Status != cmPolicies::WARN) {
583 // Stop processing if called with an INTERFACE library on the LHS.
584 if (this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
588 // Handle (additional) backward-compatibility case where the command was
589 // called with PUBLIC / INTERFACE / LINK_PUBLIC / LINK_INTERFACE_LIBRARIES.
590 // (The policy CMP0022 is not set to NEW.)
592 // Get the list of configurations considered to be DEBUG.
593 std::vector<std::string> debugConfigs =
594 this->Makefile.GetCMakeInstance()->GetDebugConfigs();
597 // Include this library in the link interface for the target.
598 if (llt == DEBUG_LibraryType || llt == GENERAL_LibraryType) {
599 // Put in the DEBUG configuration interfaces.
600 for (std::string const& dc : debugConfigs) {
601 prop = cmStrCat("LINK_INTERFACE_LIBRARIES_", dc);
602 this->AppendProperty(prop, lib);
605 if (llt == OPTIMIZED_LibraryType || llt == GENERAL_LibraryType) {
606 // Put in the non-DEBUG configuration interfaces.
607 this->AppendProperty("LINK_INTERFACE_LIBRARIES", lib);
609 // Make sure the DEBUG configuration interfaces exist so that the
610 // general one will not be used as a fall-back.
611 for (std::string const& dc : debugConfigs) {
612 prop = cmStrCat("LINK_INTERFACE_LIBRARIES_", dc);
613 if (!this->Target->GetProperty(prop)) {
614 this->Target->SetProperty(prop, "");
622 void TLL::AppendProperty(std::string const& prop, std::string const& value)
624 this->AffectsProperty(prop);
625 this->Target->AppendProperty(prop, value, this->Makefile.GetBacktrace());
628 void TLL::AffectsProperty(std::string const& prop)
630 if (!this->EncodeRemoteReference) {
633 // Add a wrapper to the expression to tell LookupLinkItem to look up
634 // names in the caller's directory.
635 if (this->Props.insert(prop).second) {
636 this->Target->AppendProperty(prop, this->DirectoryId,
637 this->Makefile.GetBacktrace());
643 for (std::string const& prop : this->Props) {
644 this->Target->AppendProperty(prop, CMAKE_DIRECTORY_ID_SEP,
645 this->Makefile.GetBacktrace());