From a5e948a36f5d1c1cf6a0ea34b04bbc4b6058e7d9 Mon Sep 17 00:00:00 2001
From: David Aguilar <davvid@gmail.com>
Date: Thu, 20 Dec 2018 16:41:04 -0800
Subject: find_package: optionally resolve symlinks when discovering packages

Teach find_package() to resolve symlinks when constructing
relocatable prefix paths from discovered cmake config files.
The `CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS` variable enables
this behavior when set to `TRUE`.

Fixes: #18704
---
 Help/command/find_package.rst                         |  5 +++++
 Help/manual/cmake-variables.7.rst                     |  1 +
 Help/release/dev/find-package-resolve-symlinks.rst    |  6 ++++++
 Help/variable/CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS.rst | 10 ++++++++++
 Source/cmFindPackageCommand.cxx                       | 10 ++++++++++
 Source/cmFindPackageCommand.h                         |  1 +
 6 files changed, 33 insertions(+)
 create mode 100644 Help/release/dev/find-package-resolve-symlinks.rst
 create mode 100644 Help/variable/CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS.rst

diff --git a/Help/command/find_package.rst b/Help/command/find_package.rst
index cafef8c..54d5f68 100644
--- a/Help/command/find_package.rst
+++ b/Help/command/find_package.rst
@@ -354,6 +354,11 @@ enabled.
 .. include:: FIND_XXX_ROOT.txt
 .. include:: FIND_XXX_ORDER.txt
 
+By default the value stored in the result variable will be the path at
+which the file is found.  The :variable:`CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS`
+variable may be set to ``TRUE`` before calling ``find_package`` in order
+to resolve symbolic links and store the real path to the file.
+
 Every non-REQUIRED ``find_package`` call can be disabled by setting the
 :variable:`CMAKE_DISABLE_FIND_PACKAGE_<PackageName>` variable to ``TRUE``.
 
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index e464b0c..0bbe914 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -170,6 +170,7 @@ Variables that Change Behavior
    /variable/CMAKE_FIND_NO_INSTALL_PREFIX
    /variable/CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY
    /variable/CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY
+   /variable/CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS
    /variable/CMAKE_FIND_PACKAGE_WARN_NO_MODULE
    /variable/CMAKE_FIND_ROOT_PATH
    /variable/CMAKE_FIND_ROOT_PATH_MODE_INCLUDE
diff --git a/Help/release/dev/find-package-resolve-symlinks.rst b/Help/release/dev/find-package-resolve-symlinks.rst
new file mode 100644
index 0000000..7adb9fe
--- /dev/null
+++ b/Help/release/dev/find-package-resolve-symlinks.rst
@@ -0,0 +1,6 @@
+find-package-resolve-symlinks
+-----------------------------
+
+* The :command:`find_package` command learned to optionally resolve
+  symbolic links in the paths to package configuration files.
+  See the :variable:`CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS` variable.
diff --git a/Help/variable/CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS.rst b/Help/variable/CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS.rst
new file mode 100644
index 0000000..dfbde20
--- /dev/null
+++ b/Help/variable/CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS.rst
@@ -0,0 +1,10 @@
+CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS
+-----------------------------------
+
+Set to ``TRUE`` to tell :command:`find_package` calls to resolve symbolic
+links in the value of ``<PackageName>_DIR``.
+
+This is helpful in use cases where the package search path points at a
+proxy directory in which symlinks to the real package locations appear.
+This is not enabled by default because there are also common use cases
+in which the symlinks should be preserved.
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index 8dc7ca2..2567b7a 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -95,6 +95,7 @@ cmFindPackageCommand::cmFindPackageCommand()
   this->UseLib32Paths = false;
   this->UseLib64Paths = false;
   this->UseLibx32Paths = false;
+  this->UseRealPath = false;
   this->PolicyScope = true;
   this->VersionMajor = 0;
   this->VersionMinor = 0;
@@ -195,6 +196,11 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args,
     this->NoSystemRegistry = true;
   }
 
+  // Check whether we should resolve symlinks when finding packages
+  if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS")) {
+    this->UseRealPath = true;
+  }
+
   // Check if Sorting should be enabled
   if (const char* so =
         this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_ORDER")) {
@@ -1502,6 +1508,10 @@ bool cmFindPackageCommand::FindConfigFile(std::string const& dir,
       fprintf(stderr, "Checking file [%s]\n", file.c_str());
     }
     if (cmSystemTools::FileExists(file, true) && this->CheckVersion(file)) {
+      // Allow resolving symlinks when the config file is found through a link
+      if (this->UseRealPath) {
+        file = cmSystemTools::GetRealPath(file);
+      }
       return true;
     }
   }
diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h
index 05bad49..83d8431 100644
--- a/Source/cmFindPackageCommand.h
+++ b/Source/cmFindPackageCommand.h
@@ -178,6 +178,7 @@ private:
   bool UseLib32Paths;
   bool UseLib64Paths;
   bool UseLibx32Paths;
+  bool UseRealPath;
   bool PolicyScope;
   std::string LibraryArchitecture;
   std::vector<std::string> Names;
-- 
cgit v0.12


From b773e58099b2fc8ebdf8319172fb018d0139396d Mon Sep 17 00:00:00 2001
From: David Aguilar <davvid@gmail.com>
Date: Tue, 15 Jan 2019 12:12:32 -0800
Subject: find_package: add test coverage for
 CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS

---
 Tests/FindPackageTest/CMakeLists.txt               | 31 ++++++++++++++++++++++
 .../cmake/SetFoundResolvedConfig.cmake             |  1 +
 .../find_package/PackageRoot/ResolvedConfig.cmake  |  1 +
 Tests/RunCMake/find_package/RunCMakeTest.cmake     |  3 +++
 .../find_package/SetFoundResolved-stderr.txt       | 10 +++++++
 Tests/RunCMake/find_package/SetFoundResolved.cmake | 17 ++++++++++++
 6 files changed, 63 insertions(+)
 create mode 100644 Tests/FindPackageTest/cmake/SetFoundResolvedConfig.cmake
 create mode 100644 Tests/RunCMake/find_package/PackageRoot/ResolvedConfig.cmake
 create mode 100644 Tests/RunCMake/find_package/SetFoundResolved-stderr.txt
 create mode 100644 Tests/RunCMake/find_package/SetFoundResolved.cmake

diff --git a/Tests/FindPackageTest/CMakeLists.txt b/Tests/FindPackageTest/CMakeLists.txt
index 6a80df5..f8b36c5 100644
--- a/Tests/FindPackageTest/CMakeLists.txt
+++ b/Tests/FindPackageTest/CMakeLists.txt
@@ -188,6 +188,37 @@ find_package(ArchC 3.1 EXACT NAMES zot)
 find_package(ArchD 4.0 EXACT NAMES zot)
 unset(CMAKE_LIBRARY_ARCHITECTURE)
 
+# Test find_package() with CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS enabled
+if(UNIX)
+  # Create ./symlink pointing back here.
+  execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink
+      . "${CMAKE_CURRENT_SOURCE_DIR}/symlink")
+  # Make find_package search through the symlink
+  set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/symlink")
+
+  # First, test the default behavior where symlinks are preserved.
+  set(SetFoundResolved_DIR "")
+  find_package(SetFoundResolved)
+  # The result must preserve the /symlink/ path.
+  set(SetFoundResolved_EXPECTED "${CMAKE_CURRENT_SOURCE_DIR}/symlink/cmake")
+  if(NOT "${SetFoundResolved_DIR}" STREQUAL "${SetFoundResolved_EXPECTED}")
+    message(SEND_ERROR "SetFoundResolved_DIR set by find_package() is set to \"${SetFoundResolved_DIR}\" (expected \"${SetFoundResolved_EXPECTED}\")")
+  endif()
+
+  # Resolve symlinks when finding the package.
+  set(CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS TRUE)
+  set(SetFoundResolved_DIR "")
+  find_package(SetFoundResolved)
+  # ./symlink points back here so it should be gone when resolved.
+  set(SetFoundResolved_EXPECTED "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+  if(NOT "${SetFoundResolved_DIR}" STREQUAL "${SetFoundResolved_EXPECTED}")
+    message(SEND_ERROR "SetFoundResolved_DIR set by find_package() is set to \"${SetFoundResolved_DIR}\" (expected \"${SetFoundResolved_EXPECTED}\")")
+  endif()
+  # Cleanup.
+  unset(CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS)
+  file(REMOVE "${CMAKE_CURRENT_SOURCE_DIR}/symlink")
+endif()
+
 # Test <PackageName>_DIR environment variable.
 # We erase the main prefix path to ensure the env var is used.
 set(CMAKE_PREFIX_PATH)
diff --git a/Tests/FindPackageTest/cmake/SetFoundResolvedConfig.cmake b/Tests/FindPackageTest/cmake/SetFoundResolvedConfig.cmake
new file mode 100644
index 0000000..b2cf87c
--- /dev/null
+++ b/Tests/FindPackageTest/cmake/SetFoundResolvedConfig.cmake
@@ -0,0 +1 @@
+set(SetFoundResolved_DIR "${CMAKE_CURRENT_LIST_DIR}")
diff --git a/Tests/RunCMake/find_package/PackageRoot/ResolvedConfig.cmake b/Tests/RunCMake/find_package/PackageRoot/ResolvedConfig.cmake
new file mode 100644
index 0000000..4496a05
--- /dev/null
+++ b/Tests/RunCMake/find_package/PackageRoot/ResolvedConfig.cmake
@@ -0,0 +1 @@
+set(Resolved_DIR "${CMAKE_CURRENT_LIST_DIR}")
diff --git a/Tests/RunCMake/find_package/RunCMakeTest.cmake b/Tests/RunCMake/find_package/RunCMakeTest.cmake
index e9f3558..066523e 100644
--- a/Tests/RunCMake/find_package/RunCMakeTest.cmake
+++ b/Tests/RunCMake/find_package/RunCMakeTest.cmake
@@ -26,3 +26,6 @@ run_cmake(WrongVersionConfig)
 run_cmake(CMP0084-OLD)
 run_cmake(CMP0084-WARN)
 run_cmake(CMP0084-NEW)
+if(UNIX)
+  run_cmake(SetFoundResolved)
+endif()
diff --git a/Tests/RunCMake/find_package/SetFoundResolved-stderr.txt b/Tests/RunCMake/find_package/SetFoundResolved-stderr.txt
new file mode 100644
index 0000000..ea94be5
--- /dev/null
+++ b/Tests/RunCMake/find_package/SetFoundResolved-stderr.txt
@@ -0,0 +1,10 @@
+CMake Warning at SetFoundResolved.cmake:10 \(message\):
+  .*/Tests/RunCMake/find_package/symlink
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Warning at SetFoundResolved.cmake:15 \(message\):
+  .*/Tests/RunCMake/find_package/PackageRoot
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/find_package/SetFoundResolved.cmake b/Tests/RunCMake/find_package/SetFoundResolved.cmake
new file mode 100644
index 0000000..8d56513
--- /dev/null
+++ b/Tests/RunCMake/find_package/SetFoundResolved.cmake
@@ -0,0 +1,17 @@
+# Create ./symlink pointing back here.
+execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink
+  PackageRoot "${CMAKE_CURRENT_SOURCE_DIR}/symlink")
+
+# Make find_package search through the symlink.
+set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/symlink")
+
+# Test preservation of symlinks.
+find_package(Resolved)
+message(WARNING "${Resolved_DIR}")
+
+# Test resolving symlinks.
+set(CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS ON)
+find_package(Resolved)
+message(WARNING "${Resolved_DIR}")
+
+file(REMOVE "${CMAKE_CURRENT_SOURCE_DIR}/symlink")
-- 
cgit v0.12