Prechádzať zdrojové kódy

target_link_libraries: Add LINK_(PUBLIC|PRIVATE) options

Makes it possible to specify the link dependencies and link
interfaces in one command without repetition.
Stephen Kelly 14 rokov pred
rodič
commit
914382224d

+ 42 - 10
Source/cmTargetLinkLibrariesCommand.cxx

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

+ 20 - 1
Source/cmTargetLinkLibrariesCommand.h

@@ -107,6 +107,18 @@ public:
       "Libraries specified as \"general\" (or without any keyword) are "
       "treated as if specified for both \"debug\" and \"optimized\"."
       "\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 "
       "case of mutually-dependent STATIC libraries CMake allows the graph "
       "to contain cycles (strongly connected components).  "
@@ -137,7 +149,14 @@ private:
   static const char* LinkLibraryTypeNames[3];
 
   cmTarget* Target;
-  bool DoingInterface;
+  enum ProcessingState {
+    ProcessingLinkLibraries,
+    ProcessingLinkInterface,
+    ProcessingPublicInterface,
+    ProcessingPrivateInterface
+  };
+
+  ProcessingState CurrentProcessingState;
 
   void HandleLibrary(const char* lib, cmTarget::LinkLibraryType llt);
 };

+ 58 - 0
Tests/CMakeCommands/target_link_libraries/CMakeLists.txt

@@ -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 "")

+ 7 - 0
Tests/CMakeCommands/target_link_libraries/depA.cpp

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

+ 7 - 0
Tests/CMakeCommands/target_link_libraries/depA.h

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

+ 11 - 0
Tests/CMakeCommands/target_link_libraries/depB.cpp

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

+ 7 - 0
Tests/CMakeCommands/target_link_libraries/depB.h

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

+ 13 - 0
Tests/CMakeCommands/target_link_libraries/depC.cpp

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

+ 12 - 0
Tests/CMakeCommands/target_link_libraries/depC.h

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

+ 12 - 0
Tests/CMakeCommands/target_link_libraries/targetA.cpp

@@ -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();
+}

+ 2 - 0
Tests/CMakeLists.txt

@@ -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"
   )
 
+  ADD_TEST_MACRO(CMakeCommands.target_link_libraries target_link_libraries)
+
   CONFIGURE_FILE(
     "${CMake_SOURCE_DIR}/Tests/CTestTestCrash/test.cmake.in"
     "${CMake_BINARY_DIR}/Tests/CTestTestCrash/test.cmake"