From d46bac5d38320907cc1f11a223fddd9a11c9b184 Mon Sep 17 00:00:00 2001
From: Brad King <brad.king@kitware.com>
Date: Wed, 24 Jul 2019 09:53:53 -0400
Subject: Makefile: Fix regression in dependencies on relative includes

Since commit a13a5c948e (Replace use of CollapseCombinedPath with
CollapseFullPath, 2019-03-19, v3.15.0-rc1~361^2~1), one code path now
calls `CollapseFullPath` with a base path that may be relative.
Backport KWSys commit c6f8e24a3 (SystemTools: Fix CollapseFullPath with
relative base path, 2019-07-24) to handle such base paths.

This case occurs when a build tree is placed in a directory inside a
source tree such that CMake is willing to generate a relative path from
the build tree to the source tree.  Add a test covering this case.

Fixes: #19507
---
 Source/kwsys/SystemTools.cxx                             |  7 ++++++-
 Source/kwsys/testSystemTools.cxx                         |  8 ++++++--
 Tests/RunCMake/BuildDepends/BuildUnderSource.c           |  5 +++++
 Tests/RunCMake/BuildDepends/BuildUnderSource.cmake       |  9 +++++++++
 Tests/RunCMake/BuildDepends/BuildUnderSource.step1.cmake |  3 +++
 Tests/RunCMake/BuildDepends/BuildUnderSource.step2.cmake |  3 +++
 Tests/RunCMake/BuildDepends/RunCMakeTest.cmake           | 16 +++++++++++++++-
 7 files changed, 47 insertions(+), 4 deletions(-)
 create mode 100644 Tests/RunCMake/BuildDepends/BuildUnderSource.c
 create mode 100644 Tests/RunCMake/BuildDepends/BuildUnderSource.cmake
 create mode 100644 Tests/RunCMake/BuildDepends/BuildUnderSource.step1.cmake
 create mode 100644 Tests/RunCMake/BuildDepends/BuildUnderSource.step2.cmake

diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx
index 2135913..ae7a18a 100644
--- a/Source/kwsys/SystemTools.cxx
+++ b/Source/kwsys/SystemTools.cxx
@@ -3394,8 +3394,13 @@ static void SystemToolsAppendComponents(
   static const std::string cur = ".";
   for (std::vector<std::string>::const_iterator i = first; i != last; ++i) {
     if (*i == up) {
-      if (out_components.size() > 1) {
+      // Remove the previous component if possible.  Ignore ../ components
+      // that try to go above the root.  Keep ../ components if they are
+      // at the beginning of a relative path (base path is relative).
+      if (out_components.size() > 1 && out_components.back() != up) {
         out_components.resize(out_components.size() - 1);
+      } else if (!out_components.empty() && out_components[0].empty()) {
+        out_components.emplace_back(std::move(*i));
       }
     } else if (!i->empty() && *i != cur) {
 #if __cplusplus >= 201103L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201103L)
diff --git a/Source/kwsys/testSystemTools.cxx b/Source/kwsys/testSystemTools.cxx
index 9a40b53..ffa6a29 100644
--- a/Source/kwsys/testSystemTools.cxx
+++ b/Source/kwsys/testSystemTools.cxx
@@ -684,9 +684,10 @@ static bool CheckRelativePaths()
 }
 
 static bool CheckCollapsePath(const std::string& path,
-                              const std::string& expected)
+                              const std::string& expected,
+                              const char* base = nullptr)
 {
-  std::string result = kwsys::SystemTools::CollapseFullPath(path);
+  std::string result = kwsys::SystemTools::CollapseFullPath(path, base);
   if (!kwsys::SystemTools::ComparePath(expected, result)) {
     std::cerr << "CollapseFullPath(" << path << ")  yielded " << result
               << " instead of " << expected << std::endl;
@@ -710,6 +711,9 @@ static bool CheckCollapsePath()
   res &= CheckCollapsePath("C:/", "C:/");
   res &= CheckCollapsePath("C:/../", "C:/");
   res &= CheckCollapsePath("C:/../../", "C:/");
+  res &= CheckCollapsePath("../b", "../../b", "../");
+  res &= CheckCollapsePath("../a/../b", "../b", "../rel");
+  res &= CheckCollapsePath("a/../b", "../rel/b", "../rel");
   return res;
 }
 
diff --git a/Tests/RunCMake/BuildDepends/BuildUnderSource.c b/Tests/RunCMake/BuildDepends/BuildUnderSource.c
new file mode 100644
index 0000000..688a040
--- /dev/null
+++ b/Tests/RunCMake/BuildDepends/BuildUnderSource.c
@@ -0,0 +1,5 @@
+#include "BuildUnderSource.h"
+int main(void)
+{
+  return BUILD_UNDER_SOURCE;
+}
diff --git a/Tests/RunCMake/BuildDepends/BuildUnderSource.cmake b/Tests/RunCMake/BuildDepends/BuildUnderSource.cmake
new file mode 100644
index 0000000..aa2a44f
--- /dev/null
+++ b/Tests/RunCMake/BuildDepends/BuildUnderSource.cmake
@@ -0,0 +1,9 @@
+enable_language(C)
+include_directories(include)
+add_executable(BuildUnderSource BuildUnderSource.c)
+
+file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/check-$<LOWER_CASE:$<CONFIG>>.cmake CONTENT "
+set(check_pairs
+  \"$<TARGET_FILE:BuildUnderSource>|${CMAKE_CURRENT_SOURCE_DIR}/include/BuildUnderSource.h\"
+  )
+")
diff --git a/Tests/RunCMake/BuildDepends/BuildUnderSource.step1.cmake b/Tests/RunCMake/BuildDepends/BuildUnderSource.step1.cmake
new file mode 100644
index 0000000..2cdd32b
--- /dev/null
+++ b/Tests/RunCMake/BuildDepends/BuildUnderSource.step1.cmake
@@ -0,0 +1,3 @@
+file(WRITE "${RunCMake_TEST_SOURCE_DIR}/include/BuildUnderSource.h" [[
+#define BUILD_UNDER_SOURCE 1
+]])
diff --git a/Tests/RunCMake/BuildDepends/BuildUnderSource.step2.cmake b/Tests/RunCMake/BuildDepends/BuildUnderSource.step2.cmake
new file mode 100644
index 0000000..8e4b858
--- /dev/null
+++ b/Tests/RunCMake/BuildDepends/BuildUnderSource.step2.cmake
@@ -0,0 +1,3 @@
+file(WRITE "${RunCMake_TEST_SOURCE_DIR}/include/BuildUnderSource.h" [[
+#define BUILD_UNDER_SOURCE 2
+]])
diff --git a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake
index 3445beb..14ae243 100644
--- a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake
+++ b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake
@@ -9,7 +9,9 @@ endif()
 
 function(run_BuildDepends CASE)
   # Use a single build tree for a few tests without cleaning.
-  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${CASE}-build)
+  if(NOT RunCMake_TEST_BINARY_DIR)
+    set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${CASE}-build)
+  endif()
   set(RunCMake_TEST_NO_CLEAN 1)
   if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
     set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
@@ -44,6 +46,18 @@ endif()
 run_BuildDepends(Custom-Symbolic-and-Byproduct)
 run_BuildDepends(Custom-Always)
 
+# Test header dependencies with a build tree underneath a source tree.
+set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/BuildUnderSource")
+set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/BuildUnderSource/build")
+file(REMOVE_RECURSE "${RunCMake_TEST_SOURCE_DIR}")
+file(MAKE_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}/include")
+foreach(f CMakeLists.txt BuildUnderSource.cmake BuildUnderSource.c)
+  configure_file("${RunCMake_SOURCE_DIR}/${f}" "${RunCMake_TEST_SOURCE_DIR}/${f}" COPYONLY)
+endforeach()
+run_BuildDepends(BuildUnderSource)
+unset(RunCMake_TEST_BINARY_DIR)
+unset(RunCMake_TEST_SOURCE_DIR)
+
 if(RunCMake_GENERATOR MATCHES "Make")
   run_BuildDepends(MakeCustomIncludes)
   if(NOT "${RunCMake_BINARY_DIR}" STREQUAL "${RunCMake_SOURCE_DIR}")
-- 
cgit v0.12