Browse Source

Merge topic 'link-implicit-libs-full-path'

882f48e5 Link libraries by full path even in implicit directories
318cd370 Help: Add link target for Find Modules section of cmake-developer.7
1535dcd8 Tests: Teach RunCMake to optionally merge command output to stdout
Brad King 10 years ago
parent
commit
1a1bdbfd87
30 changed files with 243 additions and 8 deletions
  1. 11 5
      Help/command/target_link_libraries.rst
  2. 1 0
      Help/manual/cmake-developer.7.rst
  3. 1 0
      Help/manual/cmake-policies.7.rst
  4. 63 0
      Help/policy/CMP0060.rst
  5. 6 0
      Help/release/dev/link-implicit-libs-full-path.rst
  6. 2 0
      Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst
  7. 42 0
      Source/cmComputeLinkInformation.cxx
  8. 4 0
      Source/cmComputeLinkInformation.h
  9. 5 0
      Source/cmPolicies.cxx
  10. 1 0
      Source/cmPolicies.h
  11. 2 1
      Source/cmTarget.h
  12. 35 0
      Tests/RunCMake/CMP0060/CMP0060-Common.cmake
  13. 2 0
      Tests/RunCMake/CMP0060/CMP0060-NEW.cmake
  14. 1 0
      Tests/RunCMake/CMP0060/CMP0060-OLD-Build-result.txt
  15. 1 0
      Tests/RunCMake/CMP0060/CMP0060-OLD-Build-stdout.txt
  16. 2 0
      Tests/RunCMake/CMP0060/CMP0060-OLD.cmake
  17. 1 0
      Tests/RunCMake/CMP0060/CMP0060-WARN-OFF-Build-result.txt
  18. 1 0
      Tests/RunCMake/CMP0060/CMP0060-WARN-OFF-Build-stdout.txt
  19. 1 0
      Tests/RunCMake/CMP0060/CMP0060-WARN-OFF.cmake
  20. 1 0
      Tests/RunCMake/CMP0060/CMP0060-WARN-ON-Build-result.txt
  21. 1 0
      Tests/RunCMake/CMP0060/CMP0060-WARN-ON-Build-stdout.txt
  22. 16 0
      Tests/RunCMake/CMP0060/CMP0060-WARN-ON-stderr.txt
  23. 2 0
      Tests/RunCMake/CMP0060/CMP0060-WARN-ON.cmake
  24. 3 0
      Tests/RunCMake/CMP0060/CMakeLists.txt
  25. 19 0
      Tests/RunCMake/CMP0060/RunCMakeTest.cmake
  26. 4 0
      Tests/RunCMake/CMP0060/cmp0060.c
  27. 5 0
      Tests/RunCMake/CMP0060/main.c
  28. 1 0
      Tests/RunCMake/CMakeLists.txt
  29. 8 2
      Tests/RunCMake/RunCMake.cmake
  30. 1 0
      Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt

+ 11 - 5
Help/command/target_link_libraries.rst

@@ -34,14 +34,20 @@ Each ``<item>`` may be:
   automatically be added in the build system to make sure the named
   library target is up-to-date before the ``<target>`` links.
 
+  If an imported library has the :prop_tgt:`IMPORTED_NO_SONAME`
+  target property set, CMake may ask the linker to search for
+  the library instead of using the full path
+  (e.g. ``/usr/lib/libfoo.so`` becomes ``-lfoo``).
+
 * **A full path to a library file**: The generated link line will
-  normally preserve the full path to the file.  However, there are
-  some cases where CMake must ask the linker to search for the library
-  (e.g. ``/usr/lib/libfoo.so`` becomes ``-lfoo``), such as when it
-  appears in a system library directory that the compiler front-end
-  may replace with an alternative.  Either way, the buildsystem will
+  normally preserve the full path to the file. The buildsystem will
   have a dependency to re-link ``<target>`` if the library file changes.
 
+  There are some cases where CMake may ask the linker to search for
+  the library (e.g. ``/usr/lib/libfoo.so`` becomes ``-lfoo``), such
+  as when a shared library is detected to have no ``SONAME`` field.
+  See policy :policy:`CMP0060` for discussion of another case.
+
   If the library file is in a Mac OSX framework, the ``Headers`` directory
   of the framework will also be processed as a
   :ref:`usage requirement <Target Usage Requirements>`.  This has the same

+ 1 - 0
Help/manual/cmake-developer.7.rst

@@ -605,6 +605,7 @@ have a .cmake file in this directory NOT show up in the modules
 documentation, simply leave out the ``Help/module/<module-name>.rst``
 file and the ``Help/manual/cmake-modules.7.rst`` toctree entry.
 
+.. _`Find Modules`:
 
 Find Modules
 ------------

+ 1 - 0
Help/manual/cmake-policies.7.rst

@@ -117,3 +117,4 @@ All Policies
    /policy/CMP0057
    /policy/CMP0058
    /policy/CMP0059
+   /policy/CMP0060

+ 63 - 0
Help/policy/CMP0060.rst

@@ -0,0 +1,63 @@
+CMP0060
+-------
+
+Link libraries by full path even in implicit directories.
+
+Policy :policy:`CMP0003` was introduced with the intention of always
+linking library files by full path when a full path is given to the
+:command:`target_link_libraries` command.  However, on some platforms
+(e.g. HP-UX) the compiler front-end adds alternative library search paths
+for the current architecture (e.g. ``/usr/lib/<arch>`` has alternatives
+to libraries in ``/usr/lib`` for the current architecture).
+On such platforms the :command:`find_library` may find a library such as
+``/usr/lib/libfoo.so`` that does not belong to the current architecture.
+
+Prior to policy :policy:`CMP0003` projects would still build in such
+cases because the incorrect library path would be converted to ``-lfoo``
+on the link line and the linker would find the proper library in the
+arch-specific search path provided by the compiler front-end implicitly.
+At the time we chose to remain compatible with such projects by always
+converting library files found in implicit link directories to ``-lfoo``
+flags to ask the linker to search for them.  This approach allowed existing
+projects to continue to build while still linking to libraries outside
+implicit link directories via full path (such as those in the build tree).
+
+CMake does allow projects to override this behavior by using an
+:ref:`IMPORTED library target <Imported Targets>` with its
+:prop_tgt:`IMPORTED_LOCATION` property set to the desired full path to
+a library file.  In fact, many :ref:`Find Modules` are learning to provide
+:ref:`Imported Targets` instead of just the traditional ``Foo_LIBRARIES``
+variable listing library files.  However, this makes the link line
+generated for a library found by a Find Module depend on whether it
+is linked through an imported target or not, which is inconsistent.
+Furthermore, this behavior has been a source of confusion because the
+generated link line for a library file depends on its location.  It is
+also problematic for projects trying to link statically because flags
+like ``-Wl,-Bstatic -lfoo -Wl,-Bdynamic`` may be used to help the linker
+select ``libfoo.a`` instead of ``libfoo.so`` but then leak dynamic linking
+to following libraries.  (See the :prop_tgt:`LINK_SEARCH_END_STATIC`
+target property for a solution typically used for that problem.)
+
+When the special case for libraries in implicit link directories was first
+introduced the list of implicit link directories was simply hard-coded
+(e.g. ``/lib``, ``/usr/lib``, and a few others).  Since that time, CMake
+has learned to detect the implicit link directories used by the compiler
+front-end.  If necessary, the :command:`find_library` command could be
+taught to use this information to help find libraries of the proper
+architecture.
+
+For these reasons, CMake 3.3 and above prefer to drop the special case
+and link libraries by full path even when they are in implicit link
+directories.  Policy ``CMP0060`` provides compatibility for existing
+projects.
+
+The OLD behavior for this policy is to ask the linker to search for
+libraries whose full paths are known to be in implicit link directories.
+The NEW behavior for this policy is to link libraries by full path even
+if they are in implicit link directories.
+
+This policy was introduced in CMake version 3.3.  Unlike most policies,
+CMake version |release| does *not* warn by default when this policy
+is not set and simply uses OLD behavior.  See documentation of the
+:variable:`CMAKE_POLICY_WARNING_CMP0060 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
+variable to control the warning.

+ 6 - 0
Help/release/dev/link-implicit-libs-full-path.rst

@@ -0,0 +1,6 @@
+link-implicit-libs-full-path
+----------------------------
+
+* Linking to library files by a full path in an implicit linker search
+  directory (e.g. ``/usr/lib/libfoo.a``) no longer asks the linker to
+  search for the library (e.g. ``-lfoo``).  See policy :policy:`CMP0060`.

+ 2 - 0
Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst

@@ -11,6 +11,8 @@ warn by default:
   policy :policy:`CMP0047`.
 * ``CMAKE_POLICY_WARNING_CMP0056`` controls the warning for
   policy :policy:`CMP0056`.
+* ``CMAKE_POLICY_WARNING_CMP0060`` controls the warning for
+  policy :policy:`CMP0060`.
 
 This variable should not be set by a project in CMake code.  Project
 developers running CMake may set this variable in their cache to

+ 42 - 0
Source/cmComputeLinkInformation.cxx

@@ -411,6 +411,10 @@ cmComputeLinkInformation
     std::vector<std::string> const& dirs = this->Target->GetLinkDirectories();
     this->OldLinkDirMask.insert(dirs.begin(), dirs.end());
     }
+
+  this->CMP0060Warn =
+    this->Makefile->PolicyOptionalWarningEnabled(
+      "CMAKE_POLICY_WARNING_CMP0060");
 }
 
 //----------------------------------------------------------------------------
@@ -548,6 +552,22 @@ bool cmComputeLinkInformation::Compute()
   // Add implicit language runtime libraries and directories.
   this->AddImplicitLinkInfo();
 
+  if (!this->CMP0060WarnItems.empty())
+    {
+    std::ostringstream w;
+    w << (this->Makefile->GetCMakeInstance()->GetPolicies()
+          ->GetPolicyWarning(cmPolicies::CMP0060)) << "\n"
+      "Some library files are in directories implicitly searched by "
+      "the linker when invoked for " << this->LinkLanguage << ":\n"
+      " " << cmJoin(this->CMP0060WarnItems, "\n ") << "\n"
+      "For compatibility with older versions of CMake, the generated "
+      "link line will ask the linker to search for these by library "
+      "name."
+      ;
+    this->CMakeInstance->IssueMessage(cmake::AUTHOR_WARNING, w.str(),
+                                      this->Target->GetBacktrace());
+    }
+
   return true;
 }
 
@@ -1190,6 +1210,28 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item)
     return false;
     }
 
+  // Check the policy for whether we should use the approach below.
+  switch (this->Target->GetPolicyStatusCMP0060())
+    {
+    case cmPolicies::WARN:
+      if (this->CMP0060Warn)
+        {
+        // Print the warning at most once for this item.
+        std::string const& wid = "CMP0060-WARNING-GIVEN-" + item;
+        if (!this->CMakeInstance->GetPropertyAsBool(wid))
+          {
+          this->CMakeInstance->SetProperty(wid, "1");
+          this->CMP0060WarnItems.insert(item);
+          }
+        }
+    case cmPolicies::OLD:
+      break;
+    case cmPolicies::REQUIRED_ALWAYS:
+    case cmPolicies::REQUIRED_IF_USED:
+    case cmPolicies::NEW:
+      return false;
+    }
+
   // Many system linkers support multiple architectures by
   // automatically selecting the implicit linker search path for the
   // current architecture.  If the library appears in an implicit link

+ 4 - 0
Source/cmComputeLinkInformation.h

@@ -175,6 +175,10 @@ private:
   std::vector<std::string> OldUserFlagItems;
   bool OldLinkDirMode;
 
+  // CMP0060 warnings.
+  bool CMP0060Warn;
+  std::set<std::string> CMP0060WarnItems;
+
   // Runtime path computation.
   cmOrderDirectories* OrderRuntimeSearchPath;
   void AddLibraryRuntimeInfo(std::string const& fullPath,

+ 5 - 0
Source/cmPolicies.cxx

@@ -390,6 +390,11 @@ cmPolicies::cmPolicies()
     CMP0059, "CMP0059",
     "Do no treat DEFINITIONS as a built-in directory property.",
     3,3,0, cmPolicies::WARN);
+
+  this->DefinePolicy(
+    CMP0060, "CMP0060",
+    "Link libraries by full path even in implicit directories.",
+    3,3,0, cmPolicies::WARN);
 }
 
 cmPolicies::~cmPolicies()

+ 1 - 0
Source/cmPolicies.h

@@ -118,6 +118,7 @@ public:
     CMP0058, ///< Ninja requires custom command byproducts to be explicit
     CMP0059, ///< Do not treat ``DEFINITIONS`` as a built-in directory
     /// property.
+    CMP0060, ///< Link libraries by full path even in implicit directories.
 
     /** \brief Always the last entry.
      *

+ 2 - 1
Source/cmTarget.h

@@ -34,7 +34,8 @@
   F(CMP0041) \
   F(CMP0042) \
   F(CMP0046) \
-  F(CMP0052)
+  F(CMP0052) \
+  F(CMP0060)
 
 class cmake;
 class cmMakefile;

+ 35 - 0
Tests/RunCMake/CMP0060/CMP0060-Common.cmake

@@ -0,0 +1,35 @@
+# Always build in a predictable configuration.  For multi-config
+# generators we depend on RunCMakeTest.cmake to do this for us.
+if(NOT CMAKE_CONFIGURATION_TYPES)
+  set(CMAKE_BUILD_TYPE Debug)
+endif()
+
+# Convince CMake that it can instruct the linker to search for the
+# library of the proper linkage type, but do not really pass flags.
+set(CMAKE_EXE_LINK_STATIC_C_FLAGS " ")
+set(CMAKE_EXE_LINK_DYNAMIC_C_FLAGS " ")
+
+# Make a link line asking for the linker to search for the library
+# look like a missing object file so we will get predictable content
+# in the error message.  This also ensures that cases expected to use
+# the full path can be verified by confirming that they link.
+set(CMAKE_LINK_LIBRARY_FLAG LINKFLAG_)
+set(CMAKE_LINK_LIBRARY_SUFFIX _LINKSUFFIX${CMAKE_C_OUTPUT_EXTENSION})
+
+# Convince CMake that our library is in an implicit linker search directory.
+list(APPEND CMAKE_C_IMPLICIT_LINK_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}/lib)
+
+# Create a simple library file.  Place it in our library directory.
+add_library(CMP0060 STATIC cmp0060.c)
+set_property(TARGET CMP0060 PROPERTY
+  ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}/lib)
+
+# Add a target to link the library file by full path.
+add_executable(main1 main.c)
+target_link_libraries(main1 $<TARGET_FILE:CMP0060>)
+add_dependencies(main1 CMP0060)
+
+# Add a second target to verify the warning only appears once.
+add_executable(main2 main.c)
+target_link_libraries(main2 $<TARGET_FILE:CMP0060>)
+add_dependencies(main2 CMP0060)

+ 2 - 0
Tests/RunCMake/CMP0060/CMP0060-NEW.cmake

@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0060 NEW)
+include(CMP0060-Common.cmake)

+ 1 - 0
Tests/RunCMake/CMP0060/CMP0060-OLD-Build-result.txt

@@ -0,0 +1 @@
+[^0]

+ 1 - 0
Tests/RunCMake/CMP0060/CMP0060-OLD-Build-stdout.txt

@@ -0,0 +1 @@
+LINKFLAG_CMP0060_LINKSUFFIX

+ 2 - 0
Tests/RunCMake/CMP0060/CMP0060-OLD.cmake

@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0060 OLD)
+include(CMP0060-Common.cmake)

+ 1 - 0
Tests/RunCMake/CMP0060/CMP0060-WARN-OFF-Build-result.txt

@@ -0,0 +1 @@
+[^0]

+ 1 - 0
Tests/RunCMake/CMP0060/CMP0060-WARN-OFF-Build-stdout.txt

@@ -0,0 +1 @@
+LINKFLAG_CMP0060_LINKSUFFIX

+ 1 - 0
Tests/RunCMake/CMP0060/CMP0060-WARN-OFF.cmake

@@ -0,0 +1 @@
+include(CMP0060-Common.cmake)

+ 1 - 0
Tests/RunCMake/CMP0060/CMP0060-WARN-ON-Build-result.txt

@@ -0,0 +1 @@
+[^0]

+ 1 - 0
Tests/RunCMake/CMP0060/CMP0060-WARN-ON-Build-stdout.txt

@@ -0,0 +1 @@
+LINKFLAG_CMP0060_LINKSUFFIX

+ 16 - 0
Tests/RunCMake/CMP0060/CMP0060-WARN-ON-stderr.txt

@@ -0,0 +1,16 @@
+^CMake Warning \(dev\) at CMP0060-Common.cmake:[0-9]+ \(add_executable\):
+  Policy CMP0060 is not set: Link libraries by full path even in implicit
+  directories.  Run "cmake --help-policy CMP0060" for policy details.  Use
+  the cmake_policy command to set the policy and suppress this warning.
+
+  Some library files are in directories implicitly searched by the linker
+  when invoked for C:
+
+   .*/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-build/lib/(lib)?CMP0060.(a|lib)
+
+  For compatibility with older versions of CMake, the generated link line
+  will ask the linker to search for these by library name.
+Call Stack \(most recent call first\):
+  CMP0060-WARN-ON.cmake:[0-9]+ \(include\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.$

+ 2 - 0
Tests/RunCMake/CMP0060/CMP0060-WARN-ON.cmake

@@ -0,0 +1,2 @@
+set(CMAKE_POLICY_WARNING_CMP0060 1)
+include(CMP0060-Common.cmake)

+ 3 - 0
Tests/RunCMake/CMP0060/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.2)
+project(${RunCMake_TEST} C)
+include(${RunCMake_TEST}.cmake)

+ 19 - 0
Tests/RunCMake/CMP0060/RunCMakeTest.cmake

@@ -0,0 +1,19 @@
+include(RunCMake)
+
+function(run_cmake_CMP0060 CASE)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0060-${CASE}-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+
+  run_cmake(CMP0060-${CASE})
+  set(RunCMake_TEST_OUTPUT_MERGE 1)
+  run_cmake_command(CMP0060-${CASE}-Build
+    ${CMAKE_COMMAND} --build . --config Debug
+    )
+endfunction()
+
+run_cmake_CMP0060(OLD)
+run_cmake_CMP0060(WARN-OFF)
+run_cmake_CMP0060(WARN-ON)
+run_cmake_CMP0060(NEW)

+ 4 - 0
Tests/RunCMake/CMP0060/cmp0060.c

@@ -0,0 +1,4 @@
+int libCMP0060(void)
+{
+  return 0;
+}

+ 5 - 0
Tests/RunCMake/CMP0060/main.c

@@ -0,0 +1,5 @@
+extern int libCMP0060(void);
+int main(void)
+{
+  return libCMP0060();
+}

+ 1 - 0
Tests/RunCMake/CMakeLists.txt

@@ -65,6 +65,7 @@ add_RunCMake_test(CMP0054)
 add_RunCMake_test(CMP0055)
 add_RunCMake_test(CMP0057)
 add_RunCMake_test(CMP0059)
+add_RunCMake_test(CMP0060)
 if(CMAKE_GENERATOR STREQUAL "Ninja")
   add_RunCMake_test(Ninja)
 endif()

+ 8 - 2
Tests/RunCMake/RunCMake.cmake

@@ -54,12 +54,18 @@ function(run_cmake test)
   if(RunCMake_MAKE_PROGRAM)
     list(APPEND RunCMake_TEST_OPTIONS "-DCMAKE_MAKE_PROGRAM=${RunCMake_MAKE_PROGRAM}")
   endif()
+  if(RunCMake_TEST_OUTPUT_MERGE)
+    set(actual_stderr_var actual_stdout)
+    set(actual_stderr "")
+  else()
+    set(actual_stderr_var actual_stderr)
+  endif()
   if(RunCMake_TEST_COMMAND)
     execute_process(
       COMMAND ${RunCMake_TEST_COMMAND}
       WORKING_DIRECTORY "${RunCMake_TEST_BINARY_DIR}"
       OUTPUT_VARIABLE actual_stdout
-      ERROR_VARIABLE actual_stderr
+      ERROR_VARIABLE ${actual_stderr_var}
       RESULT_VARIABLE actual_result
       )
   else()
@@ -73,7 +79,7 @@ function(run_cmake test)
                 ${RunCMake_TEST_OPTIONS}
       WORKING_DIRECTORY "${RunCMake_TEST_BINARY_DIR}"
       OUTPUT_VARIABLE actual_stdout
-      ERROR_VARIABLE actual_stderr
+      ERROR_VARIABLE ${actual_stderr_var}
       RESULT_VARIABLE actual_result
       )
   endif()

+ 1 - 0
Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt

@@ -17,6 +17,7 @@
    \* CMP0042
    \* CMP0046
    \* CMP0052
+   \* CMP0060
 
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)