target_link_libraries: Simplify implementation and add comments.

The implementation of `target_link_libraries` did grow over the years
when new features where added. This commit cleans up the implementation
and adds comments to better document its intention.

The behavior of `target_link_libraries` itself is left untouched.
This commit is contained in:
Deniz Bahadir
2017-11-27 22:30:26 +01:00
parent b0e2f1415e
commit 8112059ee7
2 changed files with 71 additions and 55 deletions

View File

@@ -25,16 +25,17 @@ const char* cmTargetLinkLibrariesCommand::LinkLibraryTypeNames[3] = {
bool cmTargetLinkLibrariesCommand::InitialPass( bool cmTargetLinkLibrariesCommand::InitialPass(
std::vector<std::string> const& args, cmExecutionStatus&) std::vector<std::string> const& args, cmExecutionStatus&)
{ {
// must have one argument // Must have at least one argument.
if (args.empty()) { if (args.empty()) {
this->SetError("called with incorrect number of arguments"); this->SetError("called with incorrect number of arguments");
return false; return false;
} }
// Alias targets cannot be on the LHS of this command.
if (this->Makefile->IsAlias(args[0])) { if (this->Makefile->IsAlias(args[0])) {
this->SetError("can not be used on an ALIAS target."); this->SetError("can not be used on an ALIAS target.");
return false; return false;
} }
// Lookup the target for which libraries are specified. // Lookup the target for which libraries are specified.
this->Target = this->Target =
this->Makefile->GetCMakeInstance()->GetGlobalGenerator()->FindTarget( this->Makefile->GetCMakeInstance()->GetGlobalGenerator()->FindTarget(
@@ -78,8 +79,7 @@ bool cmTargetLinkLibrariesCommand::InitialPass(
break; break;
} }
} }
// Now actually print the message.
// now actually print the message
switch (t) { switch (t) {
case cmake::AUTHOR_WARNING: case cmake::AUTHOR_WARNING:
this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, e.str()); this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, e.str());
@@ -94,6 +94,7 @@ bool cmTargetLinkLibrariesCommand::InitialPass(
return true; return true;
} }
// OBJECT libraries are not allowed on the LHS of the command.
if (this->Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { if (this->Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
std::ostringstream e; std::ostringstream e;
e << "Object library target \"" << args[0] << "\" " e << "Object library target \"" << args[0] << "\" "
@@ -103,6 +104,7 @@ bool cmTargetLinkLibrariesCommand::InitialPass(
return true; return true;
} }
// Having a UTILITY library on the LHS is a bug.
if (this->Target->GetType() == cmStateEnums::UTILITY) { if (this->Target->GetType() == cmStateEnums::UTILITY) {
std::ostringstream e; std::ostringstream e;
const char* modal = nullptr; const char* modal = nullptr;
@@ -129,7 +131,7 @@ bool cmTargetLinkLibrariesCommand::InitialPass(
} }
} }
// but we might not have any libs after variable expansion // But we might not have any libs after variable expansion.
if (args.size() < 2) { if (args.size() < 2) {
return true; return true;
} }
@@ -142,8 +144,8 @@ bool cmTargetLinkLibrariesCommand::InitialPass(
// specification if the keyword is encountered as the first argument. // specification if the keyword is encountered as the first argument.
this->CurrentProcessingState = ProcessingLinkLibraries; this->CurrentProcessingState = ProcessingLinkLibraries;
// add libraries, note that there is an optional prefix // Add libraries, note that there is an optional prefix
// of debug and optimized that can be used // of debug and optimized that can be used.
for (unsigned int i = 1; i < args.size(); ++i) { for (unsigned int i = 1; i < args.size(); ++i) {
if (args[i] == "LINK_INTERFACE_LIBRARIES") { if (args[i] == "LINK_INTERFACE_LIBRARIES") {
this->CurrentProcessingState = ProcessingPlainLinkInterface; this->CurrentProcessingState = ProcessingPlainLinkInterface;
@@ -366,10 +368,13 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib,
} }
} }
// Handle normal case first. // 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 (this->CurrentProcessingState != ProcessingKeywordLinkInterface && if (this->CurrentProcessingState != ProcessingKeywordLinkInterface &&
this->CurrentProcessingState != ProcessingPlainLinkInterface) { this->CurrentProcessingState != ProcessingPlainLinkInterface) {
// Assure that the target on the LHS was created in the current directory.
cmTarget* t = cmTarget* t =
this->Makefile->FindLocalNonAliasTarget(this->Target->GetName()); this->Makefile->FindLocalNonAliasTarget(this->Target->GetName());
if (!t) { if (!t) {
@@ -408,72 +413,80 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib,
} }
this->Target->AddLinkLibrary(*this->Makefile, lib, llt); this->Target->AddLinkLibrary(*this->Makefile, lib, llt);
if (this->CurrentProcessingState == ProcessingLinkLibraries) {
this->Target->AppendProperty(
"INTERFACE_LINK_LIBRARIES",
this->Target->GetDebugGeneratorExpressions(lib, llt).c_str());
return true;
}
if (this->CurrentProcessingState != ProcessingKeywordPublicInterface &&
this->CurrentProcessingState != ProcessingPlainPublicInterface) {
if (this->Target->GetType() == cmStateEnums::STATIC_LIBRARY) {
std::string configLib =
this->Target->GetDebugGeneratorExpressions(lib, llt);
if (cmGeneratorExpression::IsValidTargetName(lib) ||
cmGeneratorExpression::Find(lib) != std::string::npos) {
configLib = "$<LINK_ONLY:" + configLib + ">";
}
this->Target->AppendProperty("INTERFACE_LINK_LIBRARIES",
configLib.c_str());
}
// Not a 'public' or 'interface' library. Do not add to interface
// property.
return true;
}
} }
// 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 (this->CurrentProcessingState == ProcessingKeywordPrivateInterface ||
this->CurrentProcessingState == ProcessingPlainPrivateInterface) {
if (this->Target->GetType() == cmStateEnums::STATIC_LIBRARY) {
std::string configLib =
this->Target->GetDebugGeneratorExpressions(lib, llt);
if (cmGeneratorExpression::IsValidTargetName(lib) ||
cmGeneratorExpression::Find(lib) != std::string::npos) {
configLib = "$<LINK_ONLY:" + configLib + ">";
}
this->Target->AppendProperty("INTERFACE_LINK_LIBRARIES",
configLib.c_str());
}
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->Target->AppendProperty( this->Target->AppendProperty(
"INTERFACE_LINK_LIBRARIES", "INTERFACE_LINK_LIBRARIES",
this->Target->GetDebugGeneratorExpressions(lib, llt).c_str()); this->Target->GetDebugGeneratorExpressions(lib, llt).c_str());
// Stop processing if called without any keyword.
if (this->CurrentProcessingState == ProcessingLinkLibraries) {
return true;
}
// Stop processing if policy CMP0022 is set to NEW.
const cmPolicies::PolicyStatus policy22Status = const cmPolicies::PolicyStatus policy22Status =
this->Target->GetPolicyStatusCMP0022(); this->Target->GetPolicyStatusCMP0022();
if (policy22Status != cmPolicies::OLD && if (policy22Status != cmPolicies::OLD &&
policy22Status != cmPolicies::WARN) { policy22Status != cmPolicies::WARN) {
return true; return true;
} }
// Stop processing if called with an INTERFACE library on the LHS.
if (this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { if (this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
return true; return true;
} }
// Get the list of configurations considered to be DEBUG. // Handle (additional) backward-compatibility case where the command was
std::vector<std::string> debugConfigs = // called with PUBLIC / INTERFACE / LINK_PUBLIC / LINK_INTERFACE_LIBRARIES.
this->Makefile->GetCMakeInstance()->GetDebugConfigs(); // (The policy CMP0022 is not set to NEW.)
std::string prop; {
// 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. // Include this library in the link interface for the target.
if (llt == DEBUG_LibraryType || llt == GENERAL_LibraryType) { if (llt == DEBUG_LibraryType || llt == GENERAL_LibraryType) {
// Put in the DEBUG configuration interfaces. // Put in the DEBUG configuration interfaces.
for (std::string const& dc : debugConfigs) { for (std::string const& dc : debugConfigs) {
prop = "LINK_INTERFACE_LIBRARIES_"; prop = "LINK_INTERFACE_LIBRARIES_";
prop += dc; prop += dc;
this->Target->AppendProperty(prop, lib.c_str()); this->Target->AppendProperty(prop, lib.c_str());
}
} }
} if (llt == OPTIMIZED_LibraryType || llt == GENERAL_LibraryType) {
if (llt == OPTIMIZED_LibraryType || llt == GENERAL_LibraryType) { // Put in the non-DEBUG configuration interfaces.
// Put in the non-DEBUG configuration interfaces. this->Target->AppendProperty("LINK_INTERFACE_LIBRARIES", lib.c_str());
this->Target->AppendProperty("LINK_INTERFACE_LIBRARIES", lib.c_str());
// Make sure the DEBUG configuration interfaces exist so that the // Make sure the DEBUG configuration interfaces exist so that the
// general one will not be used as a fall-back. // general one will not be used as a fall-back.
for (std::string const& dc : debugConfigs) { for (std::string const& dc : debugConfigs) {
prop = "LINK_INTERFACE_LIBRARIES_"; prop = "LINK_INTERFACE_LIBRARIES_";
prop += dc; prop += dc;
if (!this->Target->GetProperty(prop)) { if (!this->Target->GetProperty(prop)) {
this->Target->SetProperty(prop, ""); this->Target->SetProperty(prop, "");
}
} }
} }
} }

View File

@@ -20,6 +20,9 @@ class cmTarget;
* cmTargetLinkLibrariesCommand is used to specify a list of libraries to link * cmTargetLinkLibrariesCommand is used to specify a list of libraries to link
* into executable(s) or shared objects. The names of the libraries * into executable(s) or shared objects. The names of the libraries
* should be those defined by the LIBRARY(library) command(s). * should be those defined by the LIBRARY(library) command(s).
*
* Additionally, it allows to propagate usage-requirements (including link
* libraries) from one target into another.
*/ */
class cmTargetLinkLibrariesCommand : public cmCommand class cmTargetLinkLibrariesCommand : public cmCommand
{ {