From 6199637e9540627b03d9018ff53a14f005274607 Mon Sep 17 00:00:00 2001
From: Frank Benkstein <frank@benkstein.net>
Date: Fri, 9 Nov 2018 19:14:58 +0100
Subject: configure_file: canonicalize input and output path in dependencies

Represent the input file path internally in canonical form.  Otherwise
multiple `configure_file` calls that share the same input file but specify
it relative to different directories (e.g. via `../`) result in multiple
copies of the dependency on the rule to re-run CMake.  This causes the
Ninja generator to emit duplicate phony build statements for these
dependencies, which generates an error with `-w dupbuild=err`, which
will be default in Ninja 1.9.

Also canonicalize the output path for consistency.

Add a test case.

Fixes: #18584
---
 Source/cmConfigureFileCommand.cxx                          | 14 ++++----------
 .../RunCMake/Ninja/PreventConfigureFileDupBuildRule.cmake  |  5 +++++
 Tests/RunCMake/Ninja/RunCMakeTest.cmake                    |  7 +++++++
 Tests/RunCMake/Ninja/SubDirConfigureFileDup/CMakeLists.txt |  1 +
 Tests/RunCMake/configure_file/DirInput-stderr.txt          |  2 +-
 5 files changed, 18 insertions(+), 11 deletions(-)
 create mode 100644 Tests/RunCMake/Ninja/PreventConfigureFileDupBuildRule.cmake
 create mode 100644 Tests/RunCMake/Ninja/SubDirConfigureFileDup/CMakeLists.txt

diff --git a/Source/cmConfigureFileCommand.cxx b/Source/cmConfigureFileCommand.cxx
index b5a639a..305262d 100644
--- a/Source/cmConfigureFileCommand.cxx
+++ b/Source/cmConfigureFileCommand.cxx
@@ -20,11 +20,8 @@ bool cmConfigureFileCommand::InitialPass(std::vector<std::string> const& args,
   }
 
   std::string const& inFile = args[0];
-  if (!cmSystemTools::FileIsFullPath(inFile)) {
-    this->InputFile = this->Makefile->GetCurrentSourceDirectory();
-    this->InputFile += "/";
-  }
-  this->InputFile += inFile;
+  this->InputFile = cmSystemTools::CollapseFullPath(
+    inFile, this->Makefile->GetCurrentSourceDirectory());
 
   // If the input location is a directory, error out.
   if (cmSystemTools::FileIsDirectory(this->InputFile)) {
@@ -39,11 +36,8 @@ bool cmConfigureFileCommand::InitialPass(std::vector<std::string> const& args,
   }
 
   std::string const& outFile = args[1];
-  if (!cmSystemTools::FileIsFullPath(outFile)) {
-    this->OutputFile = this->Makefile->GetCurrentBinaryDirectory();
-    this->OutputFile += "/";
-  }
-  this->OutputFile += outFile;
+  this->OutputFile = cmSystemTools::CollapseFullPath(
+    outFile, this->Makefile->GetCurrentBinaryDirectory());
 
   // If the output location is already a directory put the file in it.
   if (cmSystemTools::FileIsDirectory(this->OutputFile)) {
diff --git a/Tests/RunCMake/Ninja/PreventConfigureFileDupBuildRule.cmake b/Tests/RunCMake/Ninja/PreventConfigureFileDupBuildRule.cmake
new file mode 100644
index 0000000..505f750
--- /dev/null
+++ b/Tests/RunCMake/Ninja/PreventConfigureFileDupBuildRule.cmake
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 3.12)
+project(Test LANGUAGES C)
+
+configure_file(PreventConfigureFileDupBuildRule.cmake PreventTargetAliasesDupBuildRule.cmake @ONLY)
+add_subdirectory(SubDirConfigureFileDup)
diff --git a/Tests/RunCMake/Ninja/RunCMakeTest.cmake b/Tests/RunCMake/Ninja/RunCMakeTest.cmake
index 4b366a8..9e1e9a5 100644
--- a/Tests/RunCMake/Ninja/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Ninja/RunCMakeTest.cmake
@@ -286,3 +286,10 @@ function (run_PreventTargetAliasesDupBuildRule)
   run_ninja("${RunCMake_TEST_BINARY_DIR}" -w dupbuild=err)
 endfunction ()
 run_PreventTargetAliasesDupBuildRule()
+
+function (run_PreventConfigureFileDupBuildRule)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/PreventConfigureFileDupBuildRule-build)
+  run_cmake(PreventConfigureFileDupBuildRule)
+  run_ninja("${RunCMake_TEST_BINARY_DIR}" -w dupbuild=err)
+endfunction()
+run_PreventConfigureFileDupBuildRule()
diff --git a/Tests/RunCMake/Ninja/SubDirConfigureFileDup/CMakeLists.txt b/Tests/RunCMake/Ninja/SubDirConfigureFileDup/CMakeLists.txt
new file mode 100644
index 0000000..433f77b
--- /dev/null
+++ b/Tests/RunCMake/Ninja/SubDirConfigureFileDup/CMakeLists.txt
@@ -0,0 +1 @@
+configure_file(../PreventConfigureFileDupBuildRule.cmake PreventTargetAliasesDupBuildRule.cmake @ONLY)
diff --git a/Tests/RunCMake/configure_file/DirInput-stderr.txt b/Tests/RunCMake/configure_file/DirInput-stderr.txt
index 2e0cd14..165ad80 100644
--- a/Tests/RunCMake/configure_file/DirInput-stderr.txt
+++ b/Tests/RunCMake/configure_file/DirInput-stderr.txt
@@ -1,7 +1,7 @@
 CMake Error at DirInput.cmake:[0-9]+ \(configure_file\):
   configure_file input location
 
-    .*/Tests/RunCMake/configure_file/.
+    .*/Tests/RunCMake/configure_file
 
   is a directory but a file was expected.
 Call Stack \(most recent call first\):
-- 
cgit v0.12