From 48aac247e922409b4dfe7189521661df4b0583d1 Mon Sep 17 00:00:00 2001
From: Brad King <brad.king@kitware.com>
Date: Thu, 19 Nov 2020 16:12:01 -0500
Subject: Compile with explicit language flag when source LANGUAGE property is
 set

This change was originally made by commit 74b1c9fc8e (Explicitly specify
language flag when source LANGUAGE property is set, 2020-06-01,
v3.19.0-rc1~722^2), but it was reverted by commit 30aa715fac (Revert
"specify language flag when source LANGUAGE property is set",
2020-11-19) to restore compatibility with pre-3.19 behavior.

Implement the change again, but add policy CMP0119 to make this change
while preserving compatibility with existing projects.

Note that the `Compiler/{Clang,Intel,MSVC}-CXX` modules do not need to
specify `-TP` for their MSVC-like variants because we already use the
flag in `CMAKE_CXX_COMPILE_OBJECT`.  Similarly for `Compiler/XL-CXX`
and `Platform/Windows-Embarcadero`.

Note also that this does not seem possible to implement for XL C.
Even with `-qsourcetype=c`, `xlc` complains about an unknown suffix:
`1501-218 (W) file /.../AltExtC.zzz contains an incorrect file suffix`.
It returns non-zero even with `-qsuppress=1501-218`.

Co-Author: Robert Maynard <robert.maynard@kitware.com>
Fixes: #14516, #20716
---
 Help/manual/cmake-policies.7.rst                   |  1 +
 Help/policy/CMP0119.rst                            | 36 ++++++++++++++++++++++
 Help/prop_sf/LANGUAGE.rst                          |  8 ++++-
 Help/release/dev/explicit-LANGUAGE-flag.rst        |  5 +++
 Modules/Compiler/AppleClang-C.cmake                |  1 +
 Modules/Compiler/AppleClang-CXX.cmake              |  2 ++
 Modules/Compiler/Clang-C.cmake                     |  2 ++
 Modules/Compiler/Clang-CXX.cmake                   |  1 +
 Modules/Compiler/GNU-C.cmake                       |  1 +
 Modules/Compiler/GNU-CXX.cmake                     |  1 +
 Modules/Compiler/Intel-C.cmake                     |  3 ++
 Modules/Compiler/Intel-CXX.cmake                   |  2 ++
 Modules/Compiler/MSVC-C.cmake                      |  1 +
 Modules/Compiler/XLClang-C.cmake                   |  2 ++
 Modules/Compiler/XLClang-CXX.cmake                 |  2 ++
 Source/cmExtraSublimeTextGenerator.cxx             |  5 +++
 Source/cmGeneratorTarget.cxx                       | 24 +++++++++++++++
 Source/cmGeneratorTarget.h                         |  3 ++
 Source/cmGlobalXCodeGenerator.cxx                  |  5 +++
 Source/cmMakefileTargetGenerator.cxx               |  4 +++
 Source/cmNinjaTargetGenerator.cxx                  | 10 +++++-
 Source/cmPolicies.h                                |  9 ++++--
 Tests/RunCMake/CMP0119/AltExtC.zzz                 |  4 +++
 Tests/RunCMake/CMP0119/AltExtCXX.zzz               |  3 ++
 Tests/RunCMake/CMP0119/CMP0119-Common.cmake        |  4 +++
 Tests/RunCMake/CMP0119/CMP0119-NEW.cmake           |  6 ++++
 .../RunCMake/CMP0119/CMP0119-OLD-build-result.txt  |  1 +
 .../RunCMake/CMP0119/CMP0119-OLD-build-stderr.txt  |  1 +
 Tests/RunCMake/CMP0119/CMP0119-OLD.cmake           |  2 ++
 .../RunCMake/CMP0119/CMP0119-WARN-build-result.txt |  1 +
 .../RunCMake/CMP0119/CMP0119-WARN-build-stderr.txt |  1 +
 Tests/RunCMake/CMP0119/CMP0119-WARN.cmake          |  2 ++
 Tests/RunCMake/CMP0119/CMakeLists.txt              |  3 ++
 Tests/RunCMake/CMP0119/RunCMakeTest.cmake          | 17 ++++++++++
 Tests/RunCMake/CMakeLists.txt                      |  1 +
 .../RunCMake/TargetPolicies/PolicyList-stderr.txt  |  1 +
 Tests/SetLang/CMakeLists.txt                       |  8 +++++
 Tests/SetLang/bar.c                                | 14 ++++++++-
 Tests/SetLang/zoom.zzz                             |  7 +++++
 39 files changed, 199 insertions(+), 5 deletions(-)
 create mode 100644 Help/policy/CMP0119.rst
 create mode 100644 Help/release/dev/explicit-LANGUAGE-flag.rst
 create mode 100644 Tests/RunCMake/CMP0119/AltExtC.zzz
 create mode 100644 Tests/RunCMake/CMP0119/AltExtCXX.zzz
 create mode 100644 Tests/RunCMake/CMP0119/CMP0119-Common.cmake
 create mode 100644 Tests/RunCMake/CMP0119/CMP0119-NEW.cmake
 create mode 100644 Tests/RunCMake/CMP0119/CMP0119-OLD-build-result.txt
 create mode 100644 Tests/RunCMake/CMP0119/CMP0119-OLD-build-stderr.txt
 create mode 100644 Tests/RunCMake/CMP0119/CMP0119-OLD.cmake
 create mode 100644 Tests/RunCMake/CMP0119/CMP0119-WARN-build-result.txt
 create mode 100644 Tests/RunCMake/CMP0119/CMP0119-WARN-build-stderr.txt
 create mode 100644 Tests/RunCMake/CMP0119/CMP0119-WARN.cmake
 create mode 100644 Tests/RunCMake/CMP0119/CMakeLists.txt
 create mode 100644 Tests/RunCMake/CMP0119/RunCMakeTest.cmake
 create mode 100644 Tests/SetLang/zoom.zzz

diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst
index 6f9a24a..85b53c7 100644
--- a/Help/manual/cmake-policies.7.rst
+++ b/Help/manual/cmake-policies.7.rst
@@ -57,6 +57,7 @@ Policies Introduced by CMake 3.20
 .. toctree::
    :maxdepth: 1
 
+   CMP0119: LANGUAGE source file property explicitly compiles as language. </policy/CMP0119>
    CMP0118: The GENERATED source file property is now visible in all directories. </policy/CMP0118>
    CMP0117: MSVC RTTI flag /GR is not added to CMAKE_CXX_FLAGS by default. </policy/CMP0117>
    CMP0116: Ninja generators transform DEPFILEs from add_custom_command(). </policy/CMP0116>
diff --git a/Help/policy/CMP0119.rst b/Help/policy/CMP0119.rst
new file mode 100644
index 0000000..61c8bdc
--- /dev/null
+++ b/Help/policy/CMP0119.rst
@@ -0,0 +1,36 @@
+CMP0119
+-------
+
+.. versionadded:: 3.20
+
+:prop_sf:`LANGUAGE` source file property explicitly compiles as specified
+language.
+
+The :prop_sf:`LANGUAGE` source file property is documented to mean that the
+source file is written in the specified language.  In CMake 3.19 and below,
+setting this property causes CMake to compile the source file using the
+compiler for the specified language.  However, it only passes an explicit
+flag to tell the compiler to treat the source as the specified language
+for MSVC-like, XL, and Embarcadero compilers for the ``CXX`` language.
+CMake 3.20 and above prefer to also explicitly tell the compiler to use
+the specified language using a flag such as ``-x c`` on all compilers
+for which such flags are known.
+
+This policy provides compatibility for projects that have not been updated
+to expect this behavior.  For example, some projects were setting the
+``LANGUAGE`` property to ``C`` on assembly-language ``.S`` source files
+in order to compile them using the C compiler.  Such projects should be
+updated to use ``enable_language(ASM)``, for which CMake will often choose
+the C compiler as the assembler on relevant platforms anyway.
+
+The ``OLD`` behavior for this policy is to interpret the ``LANGUAGE <LANG>``
+property using its undocumented meaning to "use the ``<LANG>`` compiler".
+The ``NEW`` behavior for this policy is to interpret the ``LANGUAGE <LANG>``
+property using its documented meaning to "compile as a ``<LANG>`` source".
+
+This policy was introduced in CMake version 3.20.  Use the
+:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+Unlike many policies, CMake version |release| does *not* warn
+when this policy is not set and simply uses ``OLD`` behavior.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/prop_sf/LANGUAGE.rst b/Help/prop_sf/LANGUAGE.rst
index 1dd2554..f14c176 100644
--- a/Help/prop_sf/LANGUAGE.rst
+++ b/Help/prop_sf/LANGUAGE.rst
@@ -1,7 +1,7 @@
 LANGUAGE
 --------
 
-What programming language is the file.
+Specify the programming language in which a source file is written.
 
 A property that can be set to indicate what programming language the
 source file is.  If it is not set the language is determined based on
@@ -9,3 +9,9 @@ the file extension.  Typical values are ``CXX`` (i.e.  C++), ``C``,
 ``CSharp``, ``CUDA``, ``Fortran``, ``ISPC``, and ``ASM``.  Setting this
 property for a file means this file will be compiled.  Do not set this
 for headers or files that should not be compiled.
+
+.. versionchanged:: 3.20
+  Setting this property causes the source file to be compiled as the
+  specified language, using explicit flags if possible.  Previously it
+  only caused the specified language's compiler to be used.
+  See policy :policy:`CMP0119`.
diff --git a/Help/release/dev/explicit-LANGUAGE-flag.rst b/Help/release/dev/explicit-LANGUAGE-flag.rst
new file mode 100644
index 0000000..4de4a47
--- /dev/null
+++ b/Help/release/dev/explicit-LANGUAGE-flag.rst
@@ -0,0 +1,5 @@
+explicit-LANGUAGE-flag
+----------------------
+
+* The :prop_sf:`LANGUAGE` source file property now forces compilation
+  as the specified language.  See policy :policy:`CMP0119`.
diff --git a/Modules/Compiler/AppleClang-C.cmake b/Modules/Compiler/AppleClang-C.cmake
index 02d10d3..bd98193 100644
--- a/Modules/Compiler/AppleClang-C.cmake
+++ b/Modules/Compiler/AppleClang-C.cmake
@@ -12,6 +12,7 @@ if(NOT "x${CMAKE_C_SIMULATE_ID}" STREQUAL "xMSVC")
   endif()
 endif()
 
+set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c)
 
 if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.0)
   set(CMAKE_C90_STANDARD_COMPILE_OPTION "-std=c90")
diff --git a/Modules/Compiler/AppleClang-CXX.cmake b/Modules/Compiler/AppleClang-CXX.cmake
index 5282077..28be1df 100644
--- a/Modules/Compiler/AppleClang-CXX.cmake
+++ b/Modules/Compiler/AppleClang-CXX.cmake
@@ -1,6 +1,8 @@
 include(Compiler/Clang)
 __compiler_clang(CXX)
 
+set(CMAKE_CXX_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c++)
+
 if(NOT "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC")
   if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
       AND CMAKE_GENERATOR MATCHES "Makefiles"
diff --git a/Modules/Compiler/Clang-C.cmake b/Modules/Compiler/Clang-C.cmake
index a631ac0..5609abf 100644
--- a/Modules/Compiler/Clang-C.cmake
+++ b/Modules/Compiler/Clang-C.cmake
@@ -7,6 +7,7 @@ if(APPLE AND NOT appleClangPolicy STREQUAL NEW)
 endif()
 
 if("x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")
+  set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -TC)
   set(CMAKE_C_CLANG_TIDY_DRIVER_MODE "cl")
   if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
       AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
@@ -14,6 +15,7 @@ if("x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")
     set(CMAKE_C_DEPENDS_USE_COMPILER TRUE)
   endif()
 elseif("x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU")
+  set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c)
   if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
       AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
       AND CMAKE_DEPFILE_FLAGS_C)
diff --git a/Modules/Compiler/Clang-CXX.cmake b/Modules/Compiler/Clang-CXX.cmake
index 430a91c..98828e0 100644
--- a/Modules/Compiler/Clang-CXX.cmake
+++ b/Modules/Compiler/Clang-CXX.cmake
@@ -11,6 +11,7 @@ if("x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU")
     set(CMAKE_CXX_DEPENDS_USE_COMPILER TRUE)
   endif()
 
+  set(CMAKE_CXX_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c++)
   set(CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN "-fvisibility-inlines-hidden")
 endif()
 
diff --git a/Modules/Compiler/GNU-C.cmake b/Modules/Compiler/GNU-C.cmake
index 1e6b61d..86b4d83 100644
--- a/Modules/Compiler/GNU-C.cmake
+++ b/Modules/Compiler/GNU-C.cmake
@@ -10,6 +10,7 @@ if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
   set(CMAKE_C_DEPENDS_USE_COMPILER TRUE)
 endif()
 
+set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c)
 
 if (NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.5)
   set(CMAKE_C90_STANDARD_COMPILE_OPTION "-std=c90")
diff --git a/Modules/Compiler/GNU-CXX.cmake b/Modules/Compiler/GNU-CXX.cmake
index 24be2d7..758d3c7 100644
--- a/Modules/Compiler/GNU-CXX.cmake
+++ b/Modules/Compiler/GNU-CXX.cmake
@@ -10,6 +10,7 @@ if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
   set(CMAKE_CXX_DEPENDS_USE_COMPILER TRUE)
 endif()
 
+set(CMAKE_CXX_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c++)
 
 if (WIN32)
   if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
diff --git a/Modules/Compiler/Intel-C.cmake b/Modules/Compiler/Intel-C.cmake
index a7c71bd..ead9069 100644
--- a/Modules/Compiler/Intel-C.cmake
+++ b/Modules/Compiler/Intel-C.cmake
@@ -15,6 +15,7 @@ endif()
 
 if("x${CMAKE_C_SIMULATE_ID}" STREQUAL "xMSVC")
 
+  set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -TC)
   set(CMAKE_C_CLANG_TIDY_DRIVER_MODE "cl")
 
   if (NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 16.0.0)
@@ -34,6 +35,8 @@ if("x${CMAKE_C_SIMULATE_ID}" STREQUAL "xMSVC")
 
 else()
 
+  set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c)
+
   if (NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 15.0.0)
     set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std=c11")
     set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std=gnu11")
diff --git a/Modules/Compiler/Intel-CXX.cmake b/Modules/Compiler/Intel-CXX.cmake
index dd9681e..37f339a 100644
--- a/Modules/Compiler/Intel-CXX.cmake
+++ b/Modules/Compiler/Intel-CXX.cmake
@@ -48,6 +48,8 @@ if("x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC")
 
 else()
 
+  set(CMAKE_CXX_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c++)
+
   if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.0.0)
     set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std=c++20")
     set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std=gnu++20")
diff --git a/Modules/Compiler/MSVC-C.cmake b/Modules/Compiler/MSVC-C.cmake
index 31ae7fd..9a5104b 100644
--- a/Modules/Compiler/MSVC-C.cmake
+++ b/Modules/Compiler/MSVC-C.cmake
@@ -27,6 +27,7 @@ else()
   set(CMAKE_C_STANDARD_DEFAULT "")
 endif()
 
+set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -TC)
 set(CMAKE_C_CLANG_TIDY_DRIVER_MODE "cl")
 
 # There are no C compiler modes so we hard-code the known compiler supported
diff --git a/Modules/Compiler/XLClang-C.cmake b/Modules/Compiler/XLClang-C.cmake
index 54c18a6..1668a4d 100644
--- a/Modules/Compiler/XLClang-C.cmake
+++ b/Modules/Compiler/XLClang-C.cmake
@@ -1,6 +1,8 @@
 include(Compiler/XLClang)
 __compiler_xlclang(C)
 
+set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c)
+
 if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 13.1.1)
   set(CMAKE_C90_STANDARD_COMPILE_OPTION  "-std=c89")
   set(CMAKE_C90_EXTENSION_COMPILE_OPTION "-std=gnu89")
diff --git a/Modules/Compiler/XLClang-CXX.cmake b/Modules/Compiler/XLClang-CXX.cmake
index 9ea3d7c..02638c7 100644
--- a/Modules/Compiler/XLClang-CXX.cmake
+++ b/Modules/Compiler/XLClang-CXX.cmake
@@ -1,6 +1,8 @@
 include(Compiler/XLClang)
 __compiler_xlclang(CXX)
 
+set(CMAKE_CXX_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c++)
+
 if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 13.1.1)
   set(CMAKE_CXX98_STANDARD_COMPILE_OPTION  "")
   set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION "")
diff --git a/Source/cmExtraSublimeTextGenerator.cxx b/Source/cmExtraSublimeTextGenerator.cxx
index 7c36144..249dfaf 100644
--- a/Source/cmExtraSublimeTextGenerator.cxx
+++ b/Source/cmExtraSublimeTextGenerator.cxx
@@ -349,6 +349,11 @@ std::string cmExtraSublimeTextGenerator::ComputeFlagsForObject(
   if (language.empty()) {
     language = "C";
   }
+
+  // Explicitly add the explicit language flag before any other flag
+  // so user flags can override it.
+  gtgt->AddExplicitLanguageFlags(flags, *source);
+
   std::string const& config =
     lg->GetMakefile()->GetSafeDefinition("CMAKE_BUILD_TYPE");
 
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 4a79a3d..12f8e44 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -3155,6 +3155,30 @@ void cmGeneratorTarget::GetAppleArchs(const std::string& config,
   }
 }
 
+void cmGeneratorTarget::AddExplicitLanguageFlags(std::string& flags,
+                                                 cmSourceFile const& sf) const
+{
+  cmProp lang = sf.GetProperty("LANGUAGE");
+  if (!lang) {
+    return;
+  }
+
+  switch (this->GetPolicyStatusCMP0119()) {
+    case cmPolicies::WARN:
+    case cmPolicies::OLD:
+      // The OLD behavior is to not add explicit language flags.
+      return;
+    case cmPolicies::REQUIRED_ALWAYS:
+    case cmPolicies::REQUIRED_IF_USED:
+    case cmPolicies::NEW:
+      // The NEW behavior is to add explicit language flags.
+      break;
+  }
+
+  this->LocalGenerator->AppendFeatureOptions(flags, *lang,
+                                             "EXPLICIT_LANGUAGE");
+}
+
 void cmGeneratorTarget::AddCUDAArchitectureFlags(std::string& flags) const
 {
   const std::string& property = this->GetSafeProperty("CUDA_ARCHITECTURES");
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index 2517b72..cb312ad 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -447,6 +447,9 @@ public:
   void GetAppleArchs(const std::string& config,
                      std::vector<std::string>& archVec) const;
 
+  void AddExplicitLanguageFlags(std::string& flags,
+                                cmSourceFile const& sf) const;
+
   void AddCUDAArchitectureFlags(std::string& flags) const;
   void AddCUDAToolkitFlags(std::string& flags) const;
 
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index 78c7538..17d1790 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -938,6 +938,11 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFile(
     default:
       break;
   }
+
+  // Explicitly add the explicit language flag before any other flag
+  // so user flags can override it.
+  gtgt->AddExplicitLanguageFlags(flags, *sf);
+
   const std::string COMPILE_FLAGS("COMPILE_FLAGS");
   if (cmProp cflags = sf->GetProperty(COMPILE_FLAGS)) {
     lg->AppendFlags(flags, genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS));
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index 155a097..ee47e46 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -622,6 +622,10 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
   // Build the set of compiler flags.
   std::string flags;
 
+  // Explicitly add the explicit language flag before any other flag
+  // so user flags can override it.
+  this->GeneratorTarget->AddExplicitLanguageFlags(flags, source);
+
   // Add language-specific flags.
   std::string langFlags = cmStrCat("$(", lang, "_FLAGS", filterArch, ")");
   this->LocalGenerator->AppendFlags(flags, langFlags);
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index 4ac08e0..86cbf80 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -183,7 +183,15 @@ std::string cmNinjaTargetGenerator::ComputeFlagsForObject(
     }
   }
 
-  std::string flags = this->GetFlags(language, config, filterArch);
+  std::string flags;
+  // Explicitly add the explicit language flag before any other flag
+  // so user flags can override it.
+  this->GeneratorTarget->AddExplicitLanguageFlags(flags, *source);
+
+  if (!flags.empty()) {
+    flags += " ";
+  }
+  flags += this->GetFlags(language, config, filterArch);
 
   // Add Fortran format flags.
   if (language == "Fortran") {
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index 646e226..0d04f7a 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -352,7 +352,11 @@ class cmMakefile;
   SELECT(                                                                     \
     POLICY, CMP0118,                                                          \
     "The GENERATED source file property is now visible in all directories.",  \
-    3, 20, 0, cmPolicies::WARN)
+    3, 20, 0, cmPolicies::WARN)                                               \
+  SELECT(POLICY, CMP0119,                                                     \
+         "LANGUAGE source file property explicitly compiles as specified "    \
+         "language.",                                                         \
+         3, 20, 0, cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \
@@ -387,7 +391,8 @@ class cmMakefile;
   F(CMP0105)                                                                  \
   F(CMP0108)                                                                  \
   F(CMP0112)                                                                  \
-  F(CMP0113)
+  F(CMP0113)                                                                  \
+  F(CMP0119)
 
 /** \class cmPolicies
  * \brief Handles changes in CMake behavior and policies
diff --git a/Tests/RunCMake/CMP0119/AltExtC.zzz b/Tests/RunCMake/CMP0119/AltExtC.zzz
new file mode 100644
index 0000000..5b240eb
--- /dev/null
+++ b/Tests/RunCMake/CMP0119/AltExtC.zzz
@@ -0,0 +1,4 @@
+int main(void) {
+  int class = 0;
+  return class;
+}
diff --git a/Tests/RunCMake/CMP0119/AltExtCXX.zzz b/Tests/RunCMake/CMP0119/AltExtCXX.zzz
new file mode 100644
index 0000000..3c521ed
--- /dev/null
+++ b/Tests/RunCMake/CMP0119/AltExtCXX.zzz
@@ -0,0 +1,3 @@
+int main() {
+  return static_cast<int>(0);
+}
diff --git a/Tests/RunCMake/CMP0119/CMP0119-Common.cmake b/Tests/RunCMake/CMP0119/CMP0119-Common.cmake
new file mode 100644
index 0000000..f45895b
--- /dev/null
+++ b/Tests/RunCMake/CMP0119/CMP0119-Common.cmake
@@ -0,0 +1,4 @@
+enable_language(C)
+
+add_executable(AltExtC AltExtC.zzz)
+set_property(SOURCE AltExtC.zzz PROPERTY LANGUAGE C)
diff --git a/Tests/RunCMake/CMP0119/CMP0119-NEW.cmake b/Tests/RunCMake/CMP0119/CMP0119-NEW.cmake
new file mode 100644
index 0000000..df0ed48
--- /dev/null
+++ b/Tests/RunCMake/CMP0119/CMP0119-NEW.cmake
@@ -0,0 +1,6 @@
+cmake_policy(SET CMP0119 NEW)
+include(CMP0119-Common.cmake)
+
+enable_language(CXX)
+add_executable(AltExtCXX AltExtCXX.zzz)
+set_property(SOURCE AltExtCXX.zzz PROPERTY LANGUAGE CXX)
diff --git a/Tests/RunCMake/CMP0119/CMP0119-OLD-build-result.txt b/Tests/RunCMake/CMP0119/CMP0119-OLD-build-result.txt
new file mode 100644
index 0000000..d197c91
--- /dev/null
+++ b/Tests/RunCMake/CMP0119/CMP0119-OLD-build-result.txt
@@ -0,0 +1 @@
+[^0]
diff --git a/Tests/RunCMake/CMP0119/CMP0119-OLD-build-stderr.txt b/Tests/RunCMake/CMP0119/CMP0119-OLD-build-stderr.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/CMP0119/CMP0119-OLD-build-stderr.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/CMP0119/CMP0119-OLD.cmake b/Tests/RunCMake/CMP0119/CMP0119-OLD.cmake
new file mode 100644
index 0000000..9eaa200
--- /dev/null
+++ b/Tests/RunCMake/CMP0119/CMP0119-OLD.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0119 OLD)
+include(CMP0119-Common.cmake)
diff --git a/Tests/RunCMake/CMP0119/CMP0119-WARN-build-result.txt b/Tests/RunCMake/CMP0119/CMP0119-WARN-build-result.txt
new file mode 100644
index 0000000..d197c91
--- /dev/null
+++ b/Tests/RunCMake/CMP0119/CMP0119-WARN-build-result.txt
@@ -0,0 +1 @@
+[^0]
diff --git a/Tests/RunCMake/CMP0119/CMP0119-WARN-build-stderr.txt b/Tests/RunCMake/CMP0119/CMP0119-WARN-build-stderr.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/CMP0119/CMP0119-WARN-build-stderr.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/CMP0119/CMP0119-WARN.cmake b/Tests/RunCMake/CMP0119/CMP0119-WARN.cmake
new file mode 100644
index 0000000..00281e9
--- /dev/null
+++ b/Tests/RunCMake/CMP0119/CMP0119-WARN.cmake
@@ -0,0 +1,2 @@
+
+include(CMP0119-Common.cmake)
diff --git a/Tests/RunCMake/CMP0119/CMakeLists.txt b/Tests/RunCMake/CMP0119/CMakeLists.txt
new file mode 100644
index 0000000..ab1a20c
--- /dev/null
+++ b/Tests/RunCMake/CMP0119/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.19)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CMP0119/RunCMakeTest.cmake b/Tests/RunCMake/CMP0119/RunCMakeTest.cmake
new file mode 100644
index 0000000..e547ef5
--- /dev/null
+++ b/Tests/RunCMake/CMP0119/RunCMakeTest.cmake
@@ -0,0 +1,17 @@
+include(RunCMake)
+
+function(run_CMP0119 status)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0119-${status}-build)
+  run_cmake(CMP0119-${status})
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(CMP0119-${status}-build "${CMAKE_COMMAND}" --build . --config Debug)
+endfunction()
+
+if(NOT RunCMake_GENERATOR MATCHES "Visual Studio|Xcode" AND
+    NOT CMAKE_C_COMPILER_ID MATCHES "(Borland|Embarcadero|Watcom)")
+  run_CMP0119(WARN)
+  run_CMP0119(OLD)
+endif()
+if((CMAKE_C_COMPILER_ID MATCHES "(GNU|Clang|MSVC|Borland|Embarcadero|Intel|TI)"))
+  run_CMP0119(NEW)
+endif()
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 1995e4b..7f79982 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -130,6 +130,7 @@ if(CMAKE_GENERATOR MATCHES "Ninja")
   add_RunCMake_test(CMP0116)
 endif()
 add_RunCMake_test(CMP0118)
+add_RunCMake_test(CMP0119 -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID})
 
 # The test for Policy 65 requires the use of the
 # CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS variable, which both the VS and Xcode
diff --git a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
index b3f3a5e..3846d7c 100644
--- a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
+++ b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
@@ -34,6 +34,7 @@
    \* CMP0108
    \* CMP0112
    \* CMP0113
+   \* CMP0119
 
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
diff --git a/Tests/SetLang/CMakeLists.txt b/Tests/SetLang/CMakeLists.txt
index 9de4fc6..3eb9185 100644
--- a/Tests/SetLang/CMakeLists.txt
+++ b/Tests/SetLang/CMakeLists.txt
@@ -15,3 +15,11 @@ if(CMAKE_GENERATOR MATCHES "^Visual Studio" AND "x${CMAKE_C_COMPILER_ID}" STREQU
   add_library(stay stay_c.c stay_cxx.cxx)
   set_property(TARGET stay PROPERTY COMPILE_OPTIONS "-TP")
 endif()
+
+if((CMAKE_C_COMPILER_ID MATCHES "(GNU|Clang|MSVC|Borland|Embarcadero|Intel|TI|XL)"))
+  cmake_policy(SET CMP0119 NEW)
+  add_library(zoom zoom.zzz)
+  set_source_files_properties(zoom.zzz PROPERTIES LANGUAGE CXX)
+  target_link_libraries(SetLang zoom)
+  target_compile_definitions(SetLang PRIVATE WITH_ZOOM)
+endif()
diff --git a/Tests/SetLang/bar.c b/Tests/SetLang/bar.c
index b934356..515e8c2 100644
--- a/Tests/SetLang/bar.c
+++ b/Tests/SetLang/bar.c
@@ -1,10 +1,22 @@
 #include <stdio.h>
 
 int foo();
+
+#ifdef WITH_ZOOM
+int zoom();
+#endif
+
 class A
 {
 public:
-  A() { this->i = foo(); }
+  A()
+  {
+    this->i = foo();
+#ifdef WITH_ZOOM
+    i += zoom();
+    i -= zoom();
+#endif
+  }
   int i;
 };
 
diff --git a/Tests/SetLang/zoom.zzz b/Tests/SetLang/zoom.zzz
new file mode 100644
index 0000000..a0c8899
--- /dev/null
+++ b/Tests/SetLang/zoom.zzz
@@ -0,0 +1,7 @@
+int zoom()
+{
+  int r = 10;
+  r++;
+  int ret = r + 10;
+  return ret;
+}
-- 
cgit v0.12