From 0cd20e8f62328b6e3ae750dc253473cf00e05adb Mon Sep 17 00:00:00 2001
From: Marc Chevrier <marc.chevrier@gmail.com>
Date: Fri, 20 Mar 2020 15:21:32 +0100
Subject: export(): raise an error on multiple calls with same FILE

Fixes: 20472
---
 Help/manual/cmake-policies.7.rst                   |  8 +++++++
 Help/policy/CMP0103.rst                            | 22 ++++++++++++++++++
 Help/release/dev/export-multiple-calls.rst         |  5 +++++
 Source/cmExportCommand.cxx                         | 23 +++++++++++++++++++
 Source/cmPolicies.h                                |  5 ++++-
 .../RunCMake/export/Repeat-CMP0103-NEW-result.txt  |  1 +
 .../RunCMake/export/Repeat-CMP0103-NEW-stderr.txt  | 17 ++++++++++++++
 Tests/RunCMake/export/Repeat-CMP0103-NEW.cmake     |  2 ++
 Tests/RunCMake/export/Repeat-CMP0103-OLD.cmake     |  2 ++
 .../RunCMake/export/Repeat-CMP0103-WARN-stderr.txt | 26 ++++++++++++++++++++++
 Tests/RunCMake/export/Repeat-CMP0103-WARN.cmake    |  1 +
 Tests/RunCMake/export/RunCMakeTest.cmake           |  4 +++-
 12 files changed, 114 insertions(+), 2 deletions(-)
 create mode 100644 Help/policy/CMP0103.rst
 create mode 100644 Help/release/dev/export-multiple-calls.rst
 create mode 100644 Tests/RunCMake/export/Repeat-CMP0103-NEW-result.txt
 create mode 100644 Tests/RunCMake/export/Repeat-CMP0103-NEW-stderr.txt
 create mode 100644 Tests/RunCMake/export/Repeat-CMP0103-NEW.cmake
 create mode 100644 Tests/RunCMake/export/Repeat-CMP0103-OLD.cmake
 create mode 100644 Tests/RunCMake/export/Repeat-CMP0103-WARN-stderr.txt
 create mode 100644 Tests/RunCMake/export/Repeat-CMP0103-WARN.cmake

diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst
index c256250..08d59e7 100644
--- a/Help/manual/cmake-policies.7.rst
+++ b/Help/manual/cmake-policies.7.rst
@@ -51,6 +51,14 @@ The :variable:`CMAKE_MINIMUM_REQUIRED_VERSION` variable may also be used
 to determine whether to report an error on use of deprecated macros or
 functions.
 
+Policies Introduced by CMake 3.18
+=================================
+
+.. toctree::
+   :maxdepth: 1
+
+   CMP0103: Multiple export() with same FILE without APPEND is not allowed. </policy/CMP0103>
+
 Policies Introduced by CMake 3.17
 =================================
 
diff --git a/Help/policy/CMP0103.rst b/Help/policy/CMP0103.rst
new file mode 100644
index 0000000..223e0cb
--- /dev/null
+++ b/Help/policy/CMP0103.rst
@@ -0,0 +1,22 @@
+CMP0103
+-------
+
+Multiple calls to :command:`export` command with same ``FILE`` without
+``APPEND`` is no longer allowed.
+
+In CMake 3.17 and below, multiple calls to :command:`export` command with the
+same ``FILE`` without ``APPEND`` are accepted silently but only the last
+occurrence is taken into account during the generation.
+
+The ``OLD`` behavior for this policy is to ignore the multiple occurrences of
+ :command:`export` command except the last one.
+
+The ``NEW`` behavior of this policy is to raise an error on second call to
+:command:`export` command with same ``FILE`` without ``APPEND``.
+
+This policy was introduced in CMake version 3.18.  CMake version
+|release| warns when the policy is not set and uses ``OLD`` behavior.
+Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
+explicitly.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/release/dev/export-multiple-calls.rst b/Help/release/dev/export-multiple-calls.rst
new file mode 100644
index 0000000..00372ce
--- /dev/null
+++ b/Help/release/dev/export-multiple-calls.rst
@@ -0,0 +1,5 @@
+export-multiple-calls
+---------------------
+
+* The :command:`export` command now raise an error if used multiple times with
+  same ``FILE`` without ``APPEND``. See policy :policy:`CMP0103`.
diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx
index e49c174..f31759d 100644
--- a/Source/cmExportCommand.cxx
+++ b/Source/cmExportCommand.cxx
@@ -24,6 +24,7 @@
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 
@@ -183,6 +184,28 @@ bool cmExportCommand(std::vector<std::string> const& args,
     return false;
   }
 
+  // if cmExportBuildFileGenerator is already defined for the file
+  // and APPEND is not specified, if CMP0103 is OLD ignore previous definition
+  // else raise an error
+  if (gg->GetExportedTargetsFile(fname) != nullptr) {
+    switch (mf.GetPolicyStatus(cmPolicies::CMP0103)) {
+      case cmPolicies::WARN:
+        mf.IssueMessage(
+          MessageType::AUTHOR_WARNING,
+          cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0103), '\n',
+                   "export() command already specified for the file\n  ",
+                   arguments.Filename, "\nDid you miss 'APPEND' keyword?"));
+        CM_FALLTHROUGH;
+      case cmPolicies::OLD:
+        break;
+      default:
+        status.SetError(cmStrCat("command already specified for the file\n  ",
+                                 arguments.Filename,
+                                 "\nDid you miss 'APPEND' keyword?"));
+        return false;
+    }
+  }
+
   // Setup export file generation.
   std::unique_ptr<cmExportBuildFileGenerator> ebfg = nullptr;
   if (android) {
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index 1366ff0..d3daad8 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -305,7 +305,10 @@ class cmMakefile;
          17, 0, cmPolicies::WARN)                                             \
   SELECT(POLICY, CMP0102,                                                     \
          "mark_as_advanced() does nothing if a cache entry does not exist.",  \
-         3, 17, 0, cmPolicies::WARN)
+         3, 17, 0, cmPolicies::WARN)                                          \
+  SELECT(POLICY, CMP0103,                                                     \
+         "multiple export() with same FILE without APPEND is not allowed.",   \
+         3, 18, 0, cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \
diff --git a/Tests/RunCMake/export/Repeat-CMP0103-NEW-result.txt b/Tests/RunCMake/export/Repeat-CMP0103-NEW-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/export/Repeat-CMP0103-NEW-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/export/Repeat-CMP0103-NEW-stderr.txt b/Tests/RunCMake/export/Repeat-CMP0103-NEW-stderr.txt
new file mode 100644
index 0000000..48ab7b1
--- /dev/null
+++ b/Tests/RunCMake/export/Repeat-CMP0103-NEW-stderr.txt
@@ -0,0 +1,17 @@
+CMake Error at Repeat.cmake:[0-9]+ \(export\):
+  export command already specified for the file
+
+    foo.cmake
+
+  Did you miss 'APPEND' keyword\?
+Call Stack \(most recent call first\):
+  Repeat-CMP0103-NEW.cmake:[0-9]+ \(include\)
+  CMakeLists.txt:[0-9]+ \(include\)
+
+
+CMake Error at Repeat/CMakeLists.txt:[0-9]+ \(export\):
+  export command already specified for the file
+
+    .+/foo.cmake
+
+  Did you miss 'APPEND' keyword\?
diff --git a/Tests/RunCMake/export/Repeat-CMP0103-NEW.cmake b/Tests/RunCMake/export/Repeat-CMP0103-NEW.cmake
new file mode 100644
index 0000000..69381df
--- /dev/null
+++ b/Tests/RunCMake/export/Repeat-CMP0103-NEW.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0103 NEW)
+include(Repeat.cmake)
diff --git a/Tests/RunCMake/export/Repeat-CMP0103-OLD.cmake b/Tests/RunCMake/export/Repeat-CMP0103-OLD.cmake
new file mode 100644
index 0000000..25134d6
--- /dev/null
+++ b/Tests/RunCMake/export/Repeat-CMP0103-OLD.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0103 OLD)
+include(Repeat.cmake)
diff --git a/Tests/RunCMake/export/Repeat-CMP0103-WARN-stderr.txt b/Tests/RunCMake/export/Repeat-CMP0103-WARN-stderr.txt
new file mode 100644
index 0000000..3104df4
--- /dev/null
+++ b/Tests/RunCMake/export/Repeat-CMP0103-WARN-stderr.txt
@@ -0,0 +1,26 @@
+CMake Warning \(dev\) at Repeat.cmake:[0-9]+ \(export\):
+  Policy CMP0103 is not set: multiple export\(\) with same FILE without APPEND
+  is not allowed.  Run "cmake --help-policy CMP0103" for policy details.  Use
+  the cmake_policy command to set the policy and suppress this warning.
+
+  export\(\) command already specified for the file
+
+    foo.cmake
+
+  Did you miss 'APPEND' keyword\?
+Call Stack \(most recent call first\):
+  Repeat-CMP0103-WARN.cmake:[0-9]+ \(include\)
+  CMakeLists.txt:[0-9]+ \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at Repeat/CMakeLists.txt:[0-9]+ \(export\):
+  Policy CMP0103 is not set: multiple export\(\) with same FILE without APPEND
+  is not allowed.  Run "cmake --help-policy CMP0103" for policy details.  Use
+  the cmake_policy command to set the policy and suppress this warning.
+
+  export\(\) command already specified for the file
+
+    .+/foo.cmake
+
+  Did you miss 'APPEND' keyword\?
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/export/Repeat-CMP0103-WARN.cmake b/Tests/RunCMake/export/Repeat-CMP0103-WARN.cmake
new file mode 100644
index 0000000..3a630c5
--- /dev/null
+++ b/Tests/RunCMake/export/Repeat-CMP0103-WARN.cmake
@@ -0,0 +1 @@
+include(Repeat.cmake)
diff --git a/Tests/RunCMake/export/RunCMakeTest.cmake b/Tests/RunCMake/export/RunCMakeTest.cmake
index df35d95..1c74762 100644
--- a/Tests/RunCMake/export/RunCMakeTest.cmake
+++ b/Tests/RunCMake/export/RunCMakeTest.cmake
@@ -2,7 +2,9 @@ include(RunCMake)
 
 run_cmake(CustomTarget)
 run_cmake(Empty)
-run_cmake(Repeat)
+run_cmake(Repeat-CMP0103-WARN)
+run_cmake(Repeat-CMP0103-OLD)
+run_cmake(Repeat-CMP0103-NEW)
 run_cmake(TargetNotFound)
 run_cmake(AppendExport)
 run_cmake(OldIface)
-- 
cgit v0.12