|
|
@@ -2,11 +2,14 @@
|
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
|
#include "cmTargetLinkLibrariesCommand.h"
|
|
|
|
|
|
+#include <cassert>
|
|
|
#include <memory>
|
|
|
#include <sstream>
|
|
|
#include <unordered_set>
|
|
|
#include <utility>
|
|
|
|
|
|
+#include <cm/optional>
|
|
|
+
|
|
|
#include "cmExecutionStatus.h"
|
|
|
#include "cmGeneratorExpression.h"
|
|
|
#include "cmGlobalGenerator.h"
|
|
|
@@ -178,123 +181,156 @@ bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args,
|
|
|
// 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 ';'.
|
|
|
+ cm::optional<std::string> currentEntry;
|
|
|
+ auto processCurrentEntry = [&]() -> bool {
|
|
|
+ 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 (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);
|
|
|
+ if (keywords.count(args[i])) {
|
|
|
+ // A keyword argument terminates any preceding accumulated entry.
|
|
|
+ if (!processCurrentEntry()) {
|
|
|
+ return false;
|
|
|
}
|
|
|
- llt = OPTIMIZED_LibraryType;
|
|
|
- haveLLT = true;
|
|
|
- } else if (args[i] == "general") {
|
|
|
- if (haveLLT) {
|
|
|
- LinkLibraryTypeSpecifierWarning(mf, llt, GENERAL_LibraryType);
|
|
|
+
|
|
|
+ // 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;
|
|
|
}
|
|
|
- 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;
|
|
|
}
|
|
|
- } else {
|
|
|
- // Lookup old-style cache entry if type is unspecified. So if you
|
|
|
- // do a target_link_libraries(foo optimized bar) it will stay optimized
|
|
|
- // and not use the lookup. As there may be the case where someone has
|
|
|
- // specified that a library is both debug and optimized. (this check is
|
|
|
- // only there for backwards compatibility when mixing projects built
|
|
|
- // with old versions of CMake and new)
|
|
|
llt = GENERAL_LibraryType;
|
|
|
- std::string linkType = cmStrCat(args[0], "_LINK_TYPE");
|
|
|
- cmValue linkTypeString = mf.GetDefinition(linkType);
|
|
|
- if (linkTypeString) {
|
|
|
- if (*linkTypeString == "debug") {
|
|
|
- llt = DEBUG_LibraryType;
|
|
|
- }
|
|
|
- if (*linkTypeString == "optimized") {
|
|
|
- llt = OPTIMIZED_LibraryType;
|
|
|
- }
|
|
|
- }
|
|
|
- if (!tll.HandleLibrary(currentProcessingState, args[i], llt)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
+ } else {
|
|
|
+ // Accumulate this argument in the current entry.
|
|
|
+ extendCurrentEntry(args[i]);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // Process the last accumulated entry, if any.
|
|
|
+ if (!processCurrentEntry()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
// Make sure the last argument was not a library type specifier.
|
|
|
if (haveLLT) {
|
|
|
mf.IssueMessage(MessageType::FATAL_ERROR,
|