From d582c23a47ceef09e80435d6b986e3938f7f1521 Mon Sep 17 00:00:00 2001
From: Brad King <brad.king@kitware.com>
Date: Tue, 28 Jun 2016 15:10:18 -0400
Subject: try_compile: Add policy CMP0066 to honor CMAKE_<LANG>_FLAGS_<CONFIG>

In the `try_compile` source file signature we propagate the caller's
value of `CMAKE_<LANG>_FLAGS` into the test project.  Extend this to
propagate `CMAKE_<LANG>_FLAGS_<CONFIG>` too instead of always using the
default value in the test project.  This will be useful, for example, to
allow the MSVC runtime library to be changed (e.g. `-MDd` => `-MTd`).
However, some projects may currently depend on this not being done,
so we need to activate the behavior using a policy.

This change was originally made by commit v3.6.0-rc1~160^2 (try_compile:
Honor CMAKE_<LANG>_FLAGS_<CONFIG> changes, 2016-04-11) but without the
policy and so had to be reverted during the 3.6 release candidate cycle.

Fixes #16174.
---
 Help/manual/cmake-policies.7.rst               |  8 ++++
 Help/policy/CMP0066.rst                        | 27 ++++++++++++
 Help/release/dev/try_compile-config-flags.rst  |  7 ++++
 Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst |  2 +
 Source/cmCoreTryCompile.cxx                    | 37 ++++++++++++++++
 Source/cmPolicies.h                            |  5 ++-
 Tests/RunCMake/try_compile/CMP0066-stderr.txt  | 15 +++++++
 Tests/RunCMake/try_compile/CMP0066-stdout.txt  |  4 ++
 Tests/RunCMake/try_compile/CMP0066.cmake       | 58 ++++++++++++++++++++++++++
 Tests/RunCMake/try_compile/RunCMakeTest.cmake  |  1 +
 Tests/RunCMake/try_compile/src.c               |  3 ++
 11 files changed, 166 insertions(+), 1 deletion(-)
 create mode 100644 Help/policy/CMP0066.rst
 create mode 100644 Help/release/dev/try_compile-config-flags.rst
 create mode 100644 Tests/RunCMake/try_compile/CMP0066-stderr.txt
 create mode 100644 Tests/RunCMake/try_compile/CMP0066-stdout.txt
 create mode 100644 Tests/RunCMake/try_compile/CMP0066.cmake

diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst
index 43f4637..0cfe983 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.7
+================================
+
+.. toctree::
+   :maxdepth: 1
+
+   CMP0066: Honor per-config flags in try_compile() source-file signature. </policy/CMP0066>
+
 Policies Introduced by CMake 3.4
 ================================
 
diff --git a/Help/policy/CMP0066.rst b/Help/policy/CMP0066.rst
new file mode 100644
index 0000000..d1dcb0e
--- /dev/null
+++ b/Help/policy/CMP0066.rst
@@ -0,0 +1,27 @@
+CMP0066
+-------
+
+Honor per-config flags in :command:`try_compile` source-file signature.
+
+The source file signature of the :command:`try_compile` command uses the value
+of the :variable:`CMAKE_<LANG>_FLAGS` variable in the test project so that the
+test compilation works as it would in the main project.  However, CMake 3.6 and
+below do not also honor config-specific compiler flags such as those in the
+:variable:`CMAKE_<LANG>_FLAGS_DEBUG` variable.  CMake 3.7 and above prefer to
+honor config-specific compiler flags too.  This policy provides compatibility
+for projects that do not expect config-specific compiler flags to be used.
+
+The ``OLD`` behavior of this policy is to ignore config-specific flag
+variables like :variable:`CMAKE_<LANG>_FLAGS_DEBUG` and only use CMake's
+built-in defaults for the current compiler and platform.
+
+The ``NEW`` behavior of this policy is to honor config-specific flag
+variabldes like :variable:`CMAKE_<LANG>_FLAGS_DEBUG`.
+
+This policy was introduced in CMake version 3.7.  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_CMP0066 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
+variable to control the warning.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/release/dev/try_compile-config-flags.rst b/Help/release/dev/try_compile-config-flags.rst
new file mode 100644
index 0000000..ebfd6a4
--- /dev/null
+++ b/Help/release/dev/try_compile-config-flags.rst
@@ -0,0 +1,7 @@
+try_compile-config-flags
+------------------------
+
+* The :command:`try_compile` command source file signature now honors
+  configuration-specific flags (e.g. :variable:`CMAKE_<LANG>_FLAGS_DEBUG`)
+  in the generated test project.  Previously only the default such flags
+  for the current toolchain were used.  See policy :policy:`CMP0066`.
diff --git a/Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst b/Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst
index 582f9e4..36cf75f 100644
--- a/Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst
+++ b/Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst
@@ -15,6 +15,8 @@ warn by default:
   policy :policy:`CMP0060`.
 * ``CMAKE_POLICY_WARNING_CMP0065`` controls the warning for
   policy :policy:`CMP0065`.
+* ``CMAKE_POLICY_WARNING_CMP0066`` controls the warning for
+  policy :policy:`CMP0066`.
 
 This variable should not be set by a project in CMake code.  Project
 developers running CMake may set this variable in their cache to
diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx
index 805d8e8..e9367b1 100644
--- a/Source/cmCoreTryCompile.cxx
+++ b/Source/cmCoreTryCompile.cxx
@@ -334,6 +334,43 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
                     " ${COMPILE_DEFINITIONS}\")\n",
               li->c_str(), li->c_str());
     }
+    switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0066)) {
+      case cmPolicies::WARN:
+        if (this->Makefile->PolicyOptionalWarningEnabled(
+              "CMAKE_POLICY_WARNING_CMP0066")) {
+          std::ostringstream w;
+          /* clang-format off */
+          w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0066) << "\n"
+            "For compatibility with older versions of CMake, try_compile "
+            "is not honoring caller config-specific compiler flags "
+            "(e.g. CMAKE_C_FLAGS_DEBUG) in the test project."
+            ;
+          /* clang-format on */
+          this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str());
+        }
+      case cmPolicies::OLD:
+        // OLD behavior is to do nothing.
+        break;
+      case cmPolicies::REQUIRED_IF_USED:
+      case cmPolicies::REQUIRED_ALWAYS:
+        this->Makefile->IssueMessage(
+          cmake::FATAL_ERROR,
+          cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0066));
+      case cmPolicies::NEW: {
+        // NEW behavior is to pass config-specific compiler flags.
+        static std::string const cfgDefault = "DEBUG";
+        std::string const cfg =
+          !tcConfig.empty() ? cmSystemTools::UpperCase(tcConfig) : cfgDefault;
+        for (std::set<std::string>::iterator li = testLangs.begin();
+             li != testLangs.end(); ++li) {
+          std::string const langFlagsCfg = "CMAKE_" + *li + "_FLAGS_" + cfg;
+          const char* flagsCfg = this->Makefile->GetDefinition(langFlagsCfg);
+          fprintf(fout, "set(%s %s)\n", langFlagsCfg.c_str(),
+                  cmOutputConverter::EscapeForCMake(flagsCfg ? flagsCfg : "")
+                    .c_str());
+        }
+      } break;
+    }
     switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0056)) {
       case cmPolicies::WARN:
         if (this->Makefile->PolicyOptionalWarningEnabled(
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index ff49e31..0c8ff60 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -203,7 +203,10 @@ class cmPolicy;
   SELECT(POLICY, CMP0065,                                                     \
          "Do not add flags to export symbols from executables without "       \
          "the ENABLE_EXPORTS target property.",                               \
-         3, 4, 0, cmPolicies::WARN)
+         3, 4, 0, cmPolicies::WARN)                                           \
+  SELECT(POLICY, CMP0066,                                                     \
+         "Honor per-config flags in try_compile() source-file signature.", 3, \
+         7, 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/try_compile/CMP0066-stderr.txt b/Tests/RunCMake/try_compile/CMP0066-stderr.txt
new file mode 100644
index 0000000..b14e290
--- /dev/null
+++ b/Tests/RunCMake/try_compile/CMP0066-stderr.txt
@@ -0,0 +1,15 @@
+before try_compile with CMP0066 WARN-default
+after try_compile with CMP0066 WARN-default
+*
+CMake Warning \(dev\) at CMP0066.cmake:[0-9]+ \(try_compile\):
+  Policy CMP0066 is not set: Honor per-config flags in try_compile\(\)
+  source-file signature.  Run "cmake --help-policy CMP0066" for policy
+  details.  Use the cmake_policy command to set the policy and suppress this
+  warning.
+
+  For compatibility with older versions of CMake, try_compile is not honoring
+  caller config-specific compiler flags \(e.g.  CMAKE_C_FLAGS_DEBUG\) in the
+  test project.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.$
diff --git a/Tests/RunCMake/try_compile/CMP0066-stdout.txt b/Tests/RunCMake/try_compile/CMP0066-stdout.txt
new file mode 100644
index 0000000..1eb2f83
--- /dev/null
+++ b/Tests/RunCMake/try_compile/CMP0066-stdout.txt
@@ -0,0 +1,4 @@
+-- try_compile with CMP0066 WARN-default worked as expected
+-- try_compile with CMP0066 WARN-enabled worked as expected
+-- try_compile with CMP0066 OLD worked as expected
+-- try_compile with CMP0066 NEW worked as expected
diff --git a/Tests/RunCMake/try_compile/CMP0066.cmake b/Tests/RunCMake/try_compile/CMP0066.cmake
new file mode 100644
index 0000000..4b95251
--- /dev/null
+++ b/Tests/RunCMake/try_compile/CMP0066.cmake
@@ -0,0 +1,58 @@
+enable_language(C)
+set(CMAKE_C_FLAGS_RELEASE "-DPP_ERROR ${CMAKE_C_FLAGS_DEBUG}")
+set(CMAKE_TRY_COMPILE_CONFIGURATION Release)
+
+#-----------------------------------------------------------------------------
+message("before try_compile with CMP0066 WARN-default")
+try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  OUTPUT_VARIABLE out
+  )
+string(REPLACE "\n" "\n  " out "  ${out}")
+if(NOT RESULT)
+  message(FATAL_ERROR "try_compile with CMP0066 WARN-default failed but should have passed:\n${out}")
+else()
+  message(STATUS "try_compile with CMP0066 WARN-default worked as expected")
+endif()
+message("after try_compile with CMP0066 WARN-default")
+
+#-----------------------------------------------------------------------------
+set(CMAKE_POLICY_WARNING_CMP0066 ON)
+try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  OUTPUT_VARIABLE out
+  )
+string(REPLACE "\n" "\n  " out "  ${out}")
+if(NOT RESULT)
+  message(FATAL_ERROR "try_compile with CMP0066 WARN-enabled failed but should have passed:\n${out}")
+else()
+  message(STATUS "try_compile with CMP0066 WARN-enabled worked as expected")
+endif()
+
+#-----------------------------------------------------------------------------
+cmake_policy(SET CMP0066 OLD)
+try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  OUTPUT_VARIABLE out
+  )
+string(REPLACE "\n" "\n  " out "  ${out}")
+if(NOT RESULT)
+  message(FATAL_ERROR "try_compile with CMP0066 OLD failed but should have passed:\n${out}")
+else()
+  message(STATUS "try_compile with CMP0066 OLD worked as expected")
+endif()
+
+#-----------------------------------------------------------------------------
+cmake_policy(SET CMP0066 NEW)
+try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  OUTPUT_VARIABLE out
+  )
+string(REPLACE "\n" "\n  " out "  ${out}")
+if(RESULT)
+  message(FATAL_ERROR "try_compile with CMP0066 NEW passed but should have failed:\n${out}")
+elseif(NOT "x${out}" MATCHES "PP_ERROR is defined")
+  message(FATAL_ERROR "try_compile with CMP0066 NEW did not fail with PP_ERROR:\n${out}")
+else()
+  message(STATUS "try_compile with CMP0066 NEW worked as expected")
+endif()
diff --git a/Tests/RunCMake/try_compile/RunCMakeTest.cmake b/Tests/RunCMake/try_compile/RunCMakeTest.cmake
index 4f30f1d..522433a 100644
--- a/Tests/RunCMake/try_compile/RunCMakeTest.cmake
+++ b/Tests/RunCMake/try_compile/RunCMakeTest.cmake
@@ -25,6 +25,7 @@ run_cmake(TargetTypeInvalid)
 run_cmake(TargetTypeStatic)
 
 run_cmake(CMP0056)
+run_cmake(CMP0066)
 
 if(RunCMake_GENERATOR MATCHES "Make|Ninja")
   # Use a single build tree for a few tests without cleaning.
diff --git a/Tests/RunCMake/try_compile/src.c b/Tests/RunCMake/try_compile/src.c
index 8488f4e..5e51382 100644
--- a/Tests/RunCMake/try_compile/src.c
+++ b/Tests/RunCMake/try_compile/src.c
@@ -2,3 +2,6 @@ int main(void)
 {
   return 0;
 }
+#ifdef PP_ERROR
+#error PP_ERROR is defined
+#endif
-- 
cgit v0.12