target_link_libraries: Add LINK_(PUBLIC|PRIVATE) options

Makes it possible to specify the link dependencies and link
interfaces in one command without repetition.
This commit is contained in:
Stephen Kelly 2011-10-07 02:41:37 +02:00 committed by Brad King
parent cf64218eeb
commit 914382224d
11 changed files with 191 additions and 11 deletions

View File

@ -95,8 +95,8 @@ bool cmTargetLinkLibrariesCommand
bool haveLLT = false; bool haveLLT = false;
// Start with primary linking and switch to link interface // Start with primary linking and switch to link interface
// specification when the keyword is encountered. // specification if the keyword is encountered as the first argument.
this->DoingInterface = false; this->CurrentProcessingState = ProcessingLinkLibraries;
// add libraries, nothe that there is an optional prefix // add libraries, nothe that there is an optional prefix
// of debug and optimized than can be used // of debug and optimized than can be used
@ -104,7 +104,7 @@ bool cmTargetLinkLibrariesCommand
{ {
if(args[i] == "LINK_INTERFACE_LIBRARIES") if(args[i] == "LINK_INTERFACE_LIBRARIES")
{ {
this->DoingInterface = true; this->CurrentProcessingState = ProcessingLinkInterface;
if(i != 1) if(i != 1)
{ {
this->Makefile->IssueMessage( this->Makefile->IssueMessage(
@ -115,6 +115,32 @@ bool cmTargetLinkLibrariesCommand
return true; return true;
} }
} }
else if(args[i] == "LINK_PUBLIC")
{
if(i != 1 && this->CurrentProcessingState != ProcessingPrivateInterface)
{
this->Makefile->IssueMessage(
cmake::FATAL_ERROR,
"The LINK_PUBLIC or LINK_PRIVATE option must appear as the second "
"argument, just after the target name."
);
return true;
}
this->CurrentProcessingState = ProcessingPublicInterface;
}
else if(args[i] == "LINK_PRIVATE")
{
if(i != 1 && this->CurrentProcessingState != ProcessingPublicInterface)
{
this->Makefile->IssueMessage(
cmake::FATAL_ERROR,
"The LINK_PUBLIC or LINK_PRIVATE option must appear as the second "
"argument, just after the target name."
);
return true;
}
this->CurrentProcessingState = ProcessingPrivateInterface;
}
else if(args[i] == "debug") else if(args[i] == "debug")
{ {
if(haveLLT) if(haveLLT)
@ -186,11 +212,13 @@ bool cmTargetLinkLibrariesCommand
cmSystemTools::SetFatalErrorOccured(); cmSystemTools::SetFatalErrorOccured();
} }
// If the INTERFACE option was given, make sure the // If any of the LINK_ options were given, make sure the
// LINK_INTERFACE_LIBRARIES property exists. This allows the // LINK_INTERFACE_LIBRARIES target property exists.
// command to be used to specify an empty link interface. // Use of any of the new keywords implies awareness of
if(this->DoingInterface && // this property. And if no libraries are named, it should
!this->Target->GetProperty("LINK_INTERFACE_LIBRARIES")) // result in an empty link interface.
if((this->CurrentProcessingState != ProcessingLinkLibraries)
&& !this->Target->GetProperty("LINK_INTERFACE_LIBRARIES"))
{ {
this->Target->SetProperty("LINK_INTERFACE_LIBRARIES", ""); this->Target->SetProperty("LINK_INTERFACE_LIBRARIES", "");
} }
@ -217,11 +245,15 @@ cmTargetLinkLibrariesCommand::HandleLibrary(const char* lib,
cmTarget::LinkLibraryType llt) cmTarget::LinkLibraryType llt)
{ {
// Handle normal case first. // Handle normal case first.
if(!this->DoingInterface) if(this->CurrentProcessingState != ProcessingLinkInterface)
{ {
this->Makefile this->Makefile
->AddLinkLibraryForTarget(this->Target->GetName(), lib, llt); ->AddLinkLibraryForTarget(this->Target->GetName(), lib, llt);
return; if (this->CurrentProcessingState != ProcessingPublicInterface
|| this->CurrentProcessingState == ProcessingPrivateInterface)
{
return;
}
} }
// Get the list of configurations considered to be DEBUG. // Get the list of configurations considered to be DEBUG.

View File

@ -107,6 +107,18 @@ public:
"Libraries specified as \"general\" (or without any keyword) are " "Libraries specified as \"general\" (or without any keyword) are "
"treated as if specified for both \"debug\" and \"optimized\"." "treated as if specified for both \"debug\" and \"optimized\"."
"\n" "\n"
" target_link_libraries(<target>\n"
" <LINK_PRIVATE|LINK_PUBLIC>\n"
" [[debug|optimized|general] <lib>] ...\n"
" [<LINK_PRIVATE|LINK_PUBLIC>\n"
" [[debug|optimized|general] <lib>] ...])\n"
"The LINK_PUBLIC and LINK_PRIVATE modes can be used to specify both the"
"link dependencies and the link interface in one command. "
"Libraries and targets following LINK_PUBLIC are linked to, and are "
"made part of the LINK_INTERFACE_LIBRARIES. Libraries and targets "
"following LINK_PRIVATE are linked to, but are not made part of the "
"LINK_INTERFACE_LIBRARIES. "
"\n"
"The library dependency graph is normally acyclic (a DAG), but in the " "The library dependency graph is normally acyclic (a DAG), but in the "
"case of mutually-dependent STATIC libraries CMake allows the graph " "case of mutually-dependent STATIC libraries CMake allows the graph "
"to contain cycles (strongly connected components). " "to contain cycles (strongly connected components). "
@ -137,7 +149,14 @@ private:
static const char* LinkLibraryTypeNames[3]; static const char* LinkLibraryTypeNames[3];
cmTarget* Target; cmTarget* Target;
bool DoingInterface; enum ProcessingState {
ProcessingLinkLibraries,
ProcessingLinkInterface,
ProcessingPublicInterface,
ProcessingPrivateInterface
};
ProcessingState CurrentProcessingState;
void HandleLibrary(const char* lib, cmTarget::LinkLibraryType llt); void HandleLibrary(const char* lib, cmTarget::LinkLibraryType llt);
}; };

View File

@ -0,0 +1,58 @@
cmake_minimum_required(VERSION 2.8)
project(target_link_libraries)
file(WRITE
"${CMAKE_CURRENT_BINARY_DIR}/main.cxx"
"int main() { return 0; }
"
)
add_executable(
target_link_libraries
"${CMAKE_CURRENT_BINARY_DIR}/main.cxx"
)
macro(ASSERT_PROPERTY _target _property _value)
get_target_property(_out ${_target} ${_property})
if (NOT _out)
set(_out "")
endif()
if (NOT "${_out}" STREQUAL "${_value}")
message(SEND_ERROR "Target ${_target} does not have property ${_property} with value ${_value}. Actual value: ${_out}")
endif()
endmacro()
include(GenerateExportHeader)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_library(depA SHARED depA.cpp)
generate_export_header(depA)
add_library(depB SHARED depB.cpp)
generate_export_header(depB)
target_link_libraries(depB LINK_PRIVATE depA)
add_library(depC SHARED depC.cpp)
generate_export_header(depC)
target_link_libraries(depC LINK_PUBLIC depA)
assert_property(depA LINK_INTERFACE_LIBRARIES "")
assert_property(depB LINK_INTERFACE_LIBRARIES "")
assert_property(depC LINK_INTERFACE_LIBRARIES "depA")
add_executable(targetA targetA.cpp)
target_link_libraries(targetA LINK_INTERFACE_LIBRARIES depA depB)
assert_property(targetA LINK_INTERFACE_LIBRARIES "depA;depB")
set_target_properties(targetA PROPERTIES LINK_INTERFACE_LIBRARIES "")
assert_property(targetA LINK_INTERFACE_LIBRARIES "")
target_link_libraries(targetA depB depC)
assert_property(targetA LINK_INTERFACE_LIBRARIES "")

View File

@ -0,0 +1,7 @@
#include "depA.h"
int DepA::foo()
{
return 0;
}

View File

@ -0,0 +1,7 @@
#include "depa_export.h"
struct DEPA_EXPORT DepA
{
int foo();
};

View File

@ -0,0 +1,11 @@
#include "depB.h"
#include "depA.h"
int DepB::foo()
{
DepA a;
return 0;
}

View File

@ -0,0 +1,7 @@
#include "depb_export.h"
struct DEPB_EXPORT DepB
{
int foo();
};

View File

@ -0,0 +1,13 @@
#include "depC.h"
int DepC::foo()
{
return 0;
}
DepA DepC::getA()
{
DepA a;
return a;
}

View File

@ -0,0 +1,12 @@
#include "depc_export.h"
#include "depA.h"
struct DEPC_EXPORT DepC
{
int foo();
DepA getA();
};

View File

@ -0,0 +1,12 @@
#include "depB.h"
#include "depC.h"
int main(int argc, char **argv)
{
DepA a;
DepB b;
DepC c;
return a.foo() + b.foo() + c.foo();
}

View File

@ -1574,6 +1574,8 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=master -P ${CMake_SOURCE_DIR}/Utilities/
-P "${CMake_SOURCE_DIR}/Tests/CMakeCommands/build_command/RunCMake.cmake" -P "${CMake_SOURCE_DIR}/Tests/CMakeCommands/build_command/RunCMake.cmake"
) )
ADD_TEST_MACRO(CMakeCommands.target_link_libraries target_link_libraries)
CONFIGURE_FILE( CONFIGURE_FILE(
"${CMake_SOURCE_DIR}/Tests/CTestTestCrash/test.cmake.in" "${CMake_SOURCE_DIR}/Tests/CTestTestCrash/test.cmake.in"
"${CMake_BINARY_DIR}/Tests/CTestTestCrash/test.cmake" "${CMake_BINARY_DIR}/Tests/CTestTestCrash/test.cmake"