Merge topic 'FindBoost-lib-prefix-override'

ff71612d02 FindBoost: Allow Boost_LIB_PREFIX to be set externally

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !4531
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cf67a66..b6ef543 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -814,6 +814,10 @@
     PATTERN "*.sh*" PERMISSIONS OWNER_READ OWNER_EXECUTE OWNER_WRITE
                                 GROUP_READ GROUP_EXECUTE
                                 WORLD_READ WORLD_EXECUTE
+    PATTERN "ExportImportList"
+      PERMISSIONS OWNER_READ OWNER_EXECUTE OWNER_WRITE
+                  GROUP_READ GROUP_EXECUTE
+                  WORLD_READ WORLD_EXECUTE
     REGEX "Help/(dev|guide)($|/)" EXCLUDE
     )
 
diff --git a/Help/command/install.rst b/Help/command/install.rst
index abf6b17..cf04eda 100644
--- a/Help/command/install.rst
+++ b/Help/command/install.rst
@@ -644,6 +644,13 @@
 ``(IMPORTED_)?LINK_INTERFACE_LIBRARIES(_<CONFIG>)?`` to be exported, when
 policy :policy:`CMP0022` is ``NEW``.
 
+.. note::
+  The installed ``<export-name>.cmake`` file may come with additional
+  per-configuration ``<export-name>-*.cmake`` files to be loaded by
+  globbing.  Do not use an export name that is the same as the package
+  name in combination with installing a ``<package-name>-config.cmake``
+  file or the latter may be incorrectly matched by the glob and loaded.
+
 When a ``COMPONENT`` option is given, the listed ``<component>`` implicitly
 depends on all components mentioned in the export set. The exported
 ``<name>.cmake`` file will require each of the exported components to be
diff --git a/Help/generator/Ninja Multi-Config.rst b/Help/generator/Ninja Multi-Config.rst
index 1f68535..f480eb8 100644
--- a/Help/generator/Ninja Multi-Config.rst
+++ b/Help/generator/Ninja Multi-Config.rst
@@ -33,53 +33,19 @@
 The ``Ninja Multi-Config`` generator recognizes the following variables:
 
 :variable:`CMAKE_CONFIGURATION_TYPES`
-  Specifies the total set of configurations to build. See the variable's
-  documentation for more information.
+  Specifies the total set of configurations to build.
 
 :variable:`CMAKE_CROSS_CONFIGS`
   Specifies a :ref:`semicolon-separated list <CMake Language Lists>` of
   configurations available from all ``build-<Config>.ninja`` files.
-  This variable activates cross-config mode.
-  Targets from each config specified in this variable can be built from any
-  ``build-<Config>.ninja`` file. Custom commands will use the configuration
-  native to ``build-<Config>.ninja``. If it is set to ``all``, all
-  configurations from :variable:`CMAKE_CONFIGURATION_TYPES` are cross-configs.
-  If it is not specified, or empty, each ``build-<Config>.ninja`` file will
-  only contain build rules for its own configuration.
-
-  The value of this variable must be a subset of
-  :variable:`CMAKE_CONFIGURATION_TYPES`.
 
 :variable:`CMAKE_DEFAULT_BUILD_TYPE`
-  Specifies the configuration to use by default in a ``build.ninja`` file. If
-  this variable is specified, ``build.ninja`` uses build rules from
-  ``build-<Config>.ninja`` by default. All custom commands are executed with
-  this configuration. If the variable is not specified, the first item from
-  :variable:`CMAKE_CONFIGURATION_TYPES` is used instead.
-
-  The value of this variable must be one of the items from
-  :variable:`CMAKE_CONFIGURATION_TYPES`.
+  Specifies the configuration to use by default in a ``build.ninja`` file.
 
 :variable:`CMAKE_DEFAULT_CONFIGS`
   Specifies a :ref:`semicolon-separated list <CMake Language Lists>` of
   configurations to build for a target in ``build.ninja``
-  if no ``:<Config>`` suffix is specified. If it is set to ``all``, all
-  configurations from :variable:`CMAKE_CROSS_CONFIGS` are used. If
-  it is not specified, it defaults to
-  :variable:`CMAKE_DEFAULT_BUILD_TYPE`.
-
-  For example, if you set
-  :variable:`CMAKE_DEFAULT_BUILD_TYPE` to ``Release``, but
-  set :variable:`CMAKE_DEFAULT_CONFIGS` to ``Debug`` or ``all``,
-  all ``<target>`` aliases in ``build.ninja`` will resolve to
-  ``<target>:Debug`` or ``<target>:all``, but custom commands will still use
-  the ``Release`` configuration.
-
-  The value of this variable must be a subset of
-  :variable:`CMAKE_CROSS_CONFIGS` or be the same as
-  :variable:`CMAKE_DEFAULT_BUILD_TYPE`. It must not be
-  specified if :variable:`CMAKE_DEFAULT_BUILD_TYPE` or
-  :variable:`CMAKE_CROSS_CONFIGS` is not used.
+  if no ``:<Config>`` suffix is specified.
 
 Consider the following example:
 
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 @@
 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/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index e552377..cbb2298 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -127,6 +127,7 @@
    /prop_tgt/ARCHIVE_OUTPUT_DIRECTORY
    /prop_tgt/ARCHIVE_OUTPUT_NAME_CONFIG
    /prop_tgt/ARCHIVE_OUTPUT_NAME
+   /prop_tgt/PCH_WARN_INVALID
    /prop_tgt/AUTOGEN_BUILD_DIR
    /prop_tgt/AUTOGEN_ORIGIN_DEPENDS
    /prop_tgt/AUTOGEN_PARALLEL
@@ -377,6 +378,7 @@
    /prop_tgt/VS_SCC_PROVIDER
    /prop_tgt/VS_SDK_REFERENCES
    /prop_tgt/VS_SOLUTION_DEPLOY
+   /prop_tgt/VS_SOURCE_SETTINGS_tool
    /prop_tgt/VS_USER_PROPS
    /prop_tgt/VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION
    /prop_tgt/VS_WINRT_COMPONENT
@@ -486,6 +488,7 @@
    /prop_sf/VS_DEPLOYMENT_LOCATION
    /prop_sf/VS_INCLUDE_IN_VSIX
    /prop_sf/VS_RESOURCE_GENERATOR
+   /prop_sf/VS_SETTINGS
    /prop_sf/VS_SHADER_DISABLE_OPTIMIZATIONS
    /prop_sf/VS_SHADER_ENABLE_DEBUG
    /prop_sf/VS_SHADER_ENTRYPOINT
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index c271024..62c301c 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -438,6 +438,7 @@
    /variable/CMAKE_OSX_ARCHITECTURES
    /variable/CMAKE_OSX_DEPLOYMENT_TARGET
    /variable/CMAKE_OSX_SYSROOT
+   /variable/CMAKE_PCH_WARN_INVALID
    /variable/CMAKE_PDB_OUTPUT_DIRECTORY
    /variable/CMAKE_PDB_OUTPUT_DIRECTORY_CONFIG
    /variable/CMAKE_POSITION_INDEPENDENT_CODE
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/prop_sf/VS_SETTINGS.rst b/Help/prop_sf/VS_SETTINGS.rst
new file mode 100644
index 0000000..0719406
--- /dev/null
+++ b/Help/prop_sf/VS_SETTINGS.rst
@@ -0,0 +1,18 @@
+VS_SETTINGS
+-----------
+
+Set any item metadata on a non-built file.
+
+Takes a list of ``Key=Value`` pairs. Tells the Visual Studio generator to set
+``Key`` to ``Value`` as item metadata on the file.
+
+For example:
+
+.. code-block:: cmake
+
+  set_property(SOURCE file.hlsl PROPERTY VS_SETTINGS "Key=Value" "Key2=Value2")
+
+will set ``Key`` to ``Value`` and ``Key2`` to ``Value2`` on the
+``file.hlsl`` item as metadata.
+
+Generator expressions are supported.
diff --git a/Help/prop_tgt/PCH_WARN_INVALID.rst b/Help/prop_tgt/PCH_WARN_INVALID.rst
new file mode 100644
index 0000000..36ec348
--- /dev/null
+++ b/Help/prop_tgt/PCH_WARN_INVALID.rst
@@ -0,0 +1,8 @@
+PCH_WARN_INVALID
+----------------
+
+When this property is set to true, the precompile header compiler options
+will contain a compiler flag wich should warn about invalid precompiled
+headers e.g. ``-Winvalid-pch`` for GNU compiler.
+
+The defalut value is ``ON``.
diff --git a/Help/prop_tgt/VS_SOURCE_SETTINGS_tool.rst b/Help/prop_tgt/VS_SOURCE_SETTINGS_tool.rst
new file mode 100644
index 0000000..f706888
--- /dev/null
+++ b/Help/prop_tgt/VS_SOURCE_SETTINGS_tool.rst
@@ -0,0 +1,19 @@
+VS_SOURCE_SETTINGS_<tool>
+-------------------------
+
+Set any item metadata on all non-built files that use <tool>.
+
+Takes a list of ``Key=Value`` pairs. Tells the Visual Studio generator
+to set ``Key`` to ``Value`` as item metadata on all non-built files
+that use ``<tool>``.
+
+For example:
+
+.. code-block:: cmake
+
+  set_property(TARGET main PROPERTY VS_SOURCE_SETTINGS_FXCompile "Key=Value" "Key2=Value2")
+
+will set ``Key`` to ``Value`` and ``Key2`` to ``Value2`` for all
+non-built files that use ``FXCompile``.
+
+Generator expressions are supported.
diff --git a/Help/prop_tgt/XCODE_GENERATE_SCHEME.rst b/Help/prop_tgt/XCODE_GENERATE_SCHEME.rst
index 0e182cf..c32b4de 100644
--- a/Help/prop_tgt/XCODE_GENERATE_SCHEME.rst
+++ b/Help/prop_tgt/XCODE_GENERATE_SCHEME.rst
@@ -38,3 +38,4 @@
 - :prop_tgt:`XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING`
 - :prop_tgt:`XCODE_SCHEME_ENVIRONMENT`
 - :prop_tgt:`XCODE_SCHEME_EXECUTABLE`
+- :prop_tgt:`XCODE_SCHEME_WORKING_DIRECTORY`
diff --git a/Help/prop_tgt/XCODE_SCHEME_WORKING_DIRECTORY.rst b/Help/prop_tgt/XCODE_SCHEME_WORKING_DIRECTORY.rst
index 7ffa74b..f538f1d 100644
--- a/Help/prop_tgt/XCODE_SCHEME_WORKING_DIRECTORY.rst
+++ b/Help/prop_tgt/XCODE_SCHEME_WORKING_DIRECTORY.rst
@@ -1,8 +1,8 @@
 XCODE_SCHEME_WORKING_DIRECTORY
 ------------------------------
 
-Specify the ``Working Directory`` a of the `Run` and `Profile`
-action in the generated Xcode scheme. In case the value contains
+Specify the ``Working Directory`` of the *Run* and *Profile*
+actions in the generated Xcode scheme. In case the value contains
 generator expressions those are evaluated.
 
 This property is initialized by the value of the variable
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/Help/release/dev/pch-warn-invalid.rst b/Help/release/dev/pch-warn-invalid.rst
new file mode 100644
index 0000000..5fa3de7
--- /dev/null
+++ b/Help/release/dev/pch-warn-invalid.rst
@@ -0,0 +1,6 @@
+pch-warn-invalid
+----------------
+
+* The :variable:`CMAKE_PCH_WARN_INVALID` variable was added to initialize the
+  :prop_tgt:`PCH_WARN_INVALID` target property to allow the removal of the
+  precompiled header invalid warning.
diff --git a/Help/release/dev/vs-non-built-file-item-metadata.rst b/Help/release/dev/vs-non-built-file-item-metadata.rst
new file mode 100644
index 0000000..26cbad0
--- /dev/null
+++ b/Help/release/dev/vs-non-built-file-item-metadata.rst
@@ -0,0 +1,10 @@
+vs-non-built-file-item-metadata
+-------------------------------
+
+* The :prop_tgt:`VS_SOURCE_SETTINGS_<tool>` target property was added
+  to tell :ref:`Visual Studio Generators` for VS 2010 and above to add
+  metadata to non-built source files using ``<tool>``.
+
+* The :prop_sf:`VS_SETTINGS` source file property was added to tell
+  :ref:`Visual Studio Generators` for VS 2010 and above to add
+  metadata to a non-built source file.
diff --git a/Help/variable/CMAKE_CROSS_CONFIGS.rst b/Help/variable/CMAKE_CROSS_CONFIGS.rst
index c850af2..94157f3 100644
--- a/Help/variable/CMAKE_CROSS_CONFIGS.rst
+++ b/Help/variable/CMAKE_CROSS_CONFIGS.rst
@@ -3,5 +3,13 @@
 
 Specifies a :ref:`semicolon-separated list <CMake Language Lists>` of
 configurations available from all ``build-<Config>.ninja`` files in the
-:generator:`Ninja Multi-Config` generator. See the generator's
-documentation for more details.
+:generator:`Ninja Multi-Config` generator.  This variable activates
+cross-config mode. Targets from each config specified in this variable can be
+built from any ``build-<Config>.ninja`` file. Custom commands will use the
+configuration native to ``build-<Config>.ninja``. If it is set to ``all``, all
+configurations from :variable:`CMAKE_CONFIGURATION_TYPES` are cross-configs. If
+it is not specified, or empty, each ``build-<Config>.ninja`` file will only
+contain build rules for its own configuration.
+
+The value of this variable must be a subset of
+:variable:`CMAKE_CONFIGURATION_TYPES`.
diff --git a/Help/variable/CMAKE_DEFAULT_BUILD_TYPE.rst b/Help/variable/CMAKE_DEFAULT_BUILD_TYPE.rst
index 62ee0d2..aa4f82d 100644
--- a/Help/variable/CMAKE_DEFAULT_BUILD_TYPE.rst
+++ b/Help/variable/CMAKE_DEFAULT_BUILD_TYPE.rst
@@ -2,5 +2,11 @@
 ------------------------
 
 Specifies the configuration to use by default in a ``build.ninja`` file in the
-:generator:`Ninja Multi-Config` generator. See the generator's documentation
-for more details.
+:generator:`Ninja Multi-Config` generator. If this variable is specified,
+``build.ninja`` uses build rules from ``build-<Config>.ninja`` by default. All
+custom commands are executed with this configuration. If the variable is not
+specified, the first item from :variable:`CMAKE_CONFIGURATION_TYPES` is used
+instead.
+
+The value of this variable must be one of the items from
+:variable:`CMAKE_CONFIGURATION_TYPES`.
diff --git a/Help/variable/CMAKE_DEFAULT_CONFIGS.rst b/Help/variable/CMAKE_DEFAULT_CONFIGS.rst
index 86d8a5a..84c642a 100644
--- a/Help/variable/CMAKE_DEFAULT_CONFIGS.rst
+++ b/Help/variable/CMAKE_DEFAULT_CONFIGS.rst
@@ -3,5 +3,17 @@
 
 Specifies a :ref:`semicolon-separated list <CMake Language Lists>` of configurations
 to build for a target in ``build.ninja`` if no ``:<Config>`` suffix is specified in
-the :generator:`Ninja Multi-Config` generator.
-See the generator's documentation for more details.
+the :generator:`Ninja Multi-Config` generator. If it is set to ``all``, all
+configurations from :variable:`CMAKE_CROSS_CONFIGS` are used. If it is not
+specified, it defaults to :variable:`CMAKE_DEFAULT_BUILD_TYPE`.
+
+For example, if you set :variable:`CMAKE_DEFAULT_BUILD_TYPE` to ``Release``,
+but set :variable:`CMAKE_DEFAULT_CONFIGS` to ``Debug`` or ``all``, all
+``<target>`` aliases in ``build.ninja`` will resolve to ``<target>:Debug`` or
+``<target>:all``, but custom commands will still use the ``Release``
+configuration.
+
+The value of this variable must be a subset of :variable:`CMAKE_CROSS_CONFIGS`
+or be the same as :variable:`CMAKE_DEFAULT_BUILD_TYPE`. It must not be
+specified if :variable:`CMAKE_DEFAULT_BUILD_TYPE` or
+:variable:`CMAKE_CROSS_CONFIGS` is not used.
diff --git a/Help/variable/CMAKE_PCH_WARN_INVALID.rst b/Help/variable/CMAKE_PCH_WARN_INVALID.rst
new file mode 100644
index 0000000..e152abd
--- /dev/null
+++ b/Help/variable/CMAKE_PCH_WARN_INVALID.rst
@@ -0,0 +1,5 @@
+CMAKE_PCH_WARN_INVALID
+----------------------
+
+This variable is used to initialize the :prop_tgt:`PCH_WARN_INVALID`
+property of targets when they are created.
diff --git a/Help/variable/CMAKE_XCODE_SCHEME_WORKING_DIRECTORY.rst b/Help/variable/CMAKE_XCODE_SCHEME_WORKING_DIRECTORY.rst
index cc690f7..5bb7907 100644
--- a/Help/variable/CMAKE_XCODE_SCHEME_WORKING_DIRECTORY.rst
+++ b/Help/variable/CMAKE_XCODE_SCHEME_WORKING_DIRECTORY.rst
@@ -1,8 +1,8 @@
 CMAKE_XCODE_SCHEME_WORKING_DIRECTORY
 ------------------------------------
 
-Specify the ``Working Directory`` a of the `Run` and `Profile`
-action in the generated Xcode scheme.
+Specify the ``Working Directory`` of the *Run* and *Profile*
+actions in the generated Xcode scheme.
 
 This variable initializes the
 :prop_tgt:`XCODE_SCHEME_WORKING_DIRECTORY`
diff --git a/Modules/CMakeDetermineCUDACompiler.cmake b/Modules/CMakeDetermineCUDACompiler.cmake
index 62ea686..95c3cc9 100644
--- a/Modules/CMakeDetermineCUDACompiler.cmake
+++ b/Modules/CMakeDetermineCUDACompiler.cmake
@@ -74,6 +74,7 @@
 
 set(_CMAKE_PROCESSING_LANGUAGE "CUDA")
 include(CMakeFindBinUtils)
+include(Compiler/${CMAKE_CUDA_COMPILER_ID}-FindBinUtils OPTIONAL)
 unset(_CMAKE_PROCESSING_LANGUAGE)
 
 if(MSVC_CUDA_ARCHITECTURE_ID)
diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake
index 812f032..9ebaa6c 100644
--- a/Modules/CMakeDetermineCompilerId.cmake
+++ b/Modules/CMakeDetermineCompilerId.cmake
@@ -128,7 +128,7 @@
     set(CMAKE_EXECUTABLE_FORMAT "Unknown" CACHE INTERNAL "Executable file format")
   endif()
 
-  if(CMAKE_GENERATOR STREQUAL "Ninja" AND MSVC_${lang}_ARCHITECTURE_ID)
+  if(CMAKE_GENERATOR MATCHES "^Ninja" AND MSVC_${lang}_ARCHITECTURE_ID)
     foreach(userflags "${CMAKE_${lang}_COMPILER_ID_FLAGS_LIST}" "")
       CMAKE_DETERMINE_MSVC_SHOWINCLUDES_PREFIX(${lang} "${userflags}")
     endforeach()
@@ -246,8 +246,10 @@
     set(id_platform ${CMAKE_VS_PLATFORM_NAME})
     set(id_lang "${lang}")
     set(id_PostBuildEvent_Command "")
-    if(CMAKE_VS_PLATFORM_TOOLSET MATCHES "^([Ll][Ll][Vv][Mm](_v[0-9]+(_xp)?)?|[Cc][Ll][Aa][Nn][Gg][Cc][Ll])$")
+    if(CMAKE_VS_PLATFORM_TOOLSET MATCHES "^[Ll][Ll][Vv][Mm](_v[0-9]+(_xp)?)?$")
       set(id_cl_var "ClangClExecutable")
+    elseif(CMAKE_VS_PLATFORM_TOOLSET MATCHES "^[Cc][Ll][Aa][Nn][Gg][Cc][Ll]$")
+      set(id_cl "$(CLToolExe)")
     elseif(CMAKE_VS_PLATFORM_TOOLSET MATCHES "v[0-9]+_clang_.*")
       set(id_cl clang.exe)
     else()
diff --git a/Modules/CheckIPOSupported.cmake b/Modules/CheckIPOSupported.cmake
index 0d6ad20..90a9f61 100644
--- a/Modules/CheckIPOSupported.cmake
+++ b/Modules/CheckIPOSupported.cmake
@@ -113,7 +113,7 @@
   endforeach()
 
   try_compile(
-      result
+      _IPO_LANGUAGE_CHECK_RESULT
       "${bindir}"
       "${srcdir}"
       "${TRY_COMPILE_PROJECT_NAME}"
@@ -122,8 +122,10 @@
       "-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON"
       OUTPUT_VARIABLE output
   )
+  set(_IPO_LANGUAGE_CHECK_RESULT "${_IPO_LANGUAGE_CHECK_RESULT}")
+  unset(_IPO_LANGUAGE_CHECK_RESULT CACHE)
 
-  if(NOT result)
+  if(NOT _IPO_LANGUAGE_CHECK_RESULT)
     file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
       "${language} compiler IPO check failed with the following output:\n"
       "${output}\n")
diff --git a/Modules/Compiler/GNU.cmake b/Modules/Compiler/GNU.cmake
index 1c050a2..3357a86 100644
--- a/Modules/Compiler/GNU.cmake
+++ b/Modules/Compiler/GNU.cmake
@@ -114,6 +114,7 @@
   if (NOT CMAKE_GENERATOR MATCHES "Xcode")
     set(CMAKE_PCH_PROLOGUE "#pragma GCC system_header")
   endif()
-  set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -Winvalid-pch -include <PCH_HEADER>)
-  set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH -Winvalid-pch -x ${__pch_header_${lang}} -include <PCH_HEADER>)
+  set(CMAKE_${lang}_COMPILE_OPTIONS_INVALID_PCH -Winvalid-pch)
+  set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -include <PCH_HEADER>)
+  set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH -x ${__pch_header_${lang}} -include <PCH_HEADER>)
 endmacro()
diff --git a/Modules/Compiler/Intel.cmake b/Modules/Compiler/Intel.cmake
index d895ed0..63a20af 100644
--- a/Modules/Compiler/Intel.cmake
+++ b/Modules/Compiler/Intel.cmake
@@ -37,7 +37,8 @@
     set(CMAKE_PCH_EXTENSION .pchi)
     set(CMAKE_LINK_PCH ON)
     set(CMAKE_PCH_EPILOGUE "#pragma hdrstop")
-    set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -Winvalid-pch -Wno-pch-messages -pch-use <PCH_FILE> -include <PCH_HEADER>)
-    set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH -Winvalid-pch -Wno-pch-messages -pch-create <PCH_FILE> -include <PCH_HEADER>)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_INVALID_PCH -Winvalid-pch)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -Wno-pch-messages -pch-use <PCH_FILE> -include <PCH_HEADER>)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH -Wno-pch-messages -pch-create <PCH_FILE> -include <PCH_HEADER>)
   endmacro()
 endif()
diff --git a/Modules/FindPython/Support.cmake b/Modules/FindPython/Support.cmake
index 0ca1f56..68be3f9 100644
--- a/Modules/FindPython/Support.cmake
+++ b/Modules/FindPython/Support.cmake
@@ -321,7 +321,7 @@
 
   if (_${_PYTHON_PREFIX}_EXECUTABLE AND NOT CMAKE_CROSSCOMPILING)
     if (NAME STREQUAL "PREFIX")
-      execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; from distutils import sysconfig; sys.stdout.write(';'.join([sysconfig.PREFIX,sysconfig.EXEC_PREFIX,sysconfig.BASE_EXEC_PREFIX]))"
+      execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\ntry:\n   from distutils import sysconfig\n   sys.stdout.write(';'.join([sysconfig.PREFIX,sysconfig.EXEC_PREFIX,sysconfig.BASE_EXEC_PREFIX]))\nexcept Exception:\n   import sysconfig\n   sys.stdout.write(';'.join([sysconfig.get_config_var('base') or '', sysconfig.get_config_var('installed_base') or '']))"
                        RESULT_VARIABLE _result
                        OUTPUT_VARIABLE _values
                        ERROR_QUIET
@@ -332,16 +332,23 @@
         list (REMOVE_DUPLICATES _values)
       endif()
     elseif (NAME STREQUAL "INCLUDES")
-      execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; from distutils import sysconfig; sys.stdout.write(';'.join([sysconfig.get_python_inc(plat_specific=True),sysconfig.get_python_inc(plat_specific=False)]))"
+      if (WIN32)
+        set (_scheme "nt")
+      else()
+        set (_scheme "posix_prefix")
+      endif()
+      execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\ntry:\n   from distutils import sysconfig\n   sys.stdout.write(';'.join([sysconfig.get_python_inc(plat_specific=True),sysconfig.get_python_inc(plat_specific=False)]))\nexcept Exception:\n   import sysconfig\n   sys.stdout.write(';'.join([sysconfig.get_path('platinclude'),sysconfig.get_path('platinclude','${_scheme}'),sysconfig.get_path('include'),sysconfig.get_path('include','${_scheme}')]))"
                        RESULT_VARIABLE _result
                        OUTPUT_VARIABLE _values
                        ERROR_QUIET
                        OUTPUT_STRIP_TRAILING_WHITESPACE)
       if (_result)
         unset (_values)
+      else()
+        list (REMOVE_DUPLICATES _values)
       endif()
     elseif (NAME STREQUAL "SOABI")
-      execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; from distutils import sysconfig;sys.stdout.write(';'.join([sysconfig.get_config_var('SOABI') or '',sysconfig.get_config_var('EXT_SUFFIX') or '']))"
+      execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\ntry:\n   from distutils import sysconfig\n   sys.stdout.write(';'.join([sysconfig.get_config_var('SOABI') or '',sysconfig.get_config_var('EXT_SUFFIX') or '']))\nexcept Exception:\n   import sysconfig;sys.stdout.write(';'.join([sysconfig.get_config_var('SOABI') or '',sysconfig.get_config_var('EXT_SUFFIX') or '']))"
                        RESULT_VARIABLE _result
                        OUTPUT_VARIABLE _soabi
                        ERROR_QUIET
@@ -349,14 +356,15 @@
       if (_result)
         unset (_values)
       else()
-        list (GET _soabi 0 _values)
-        if (NOT _values)
-          # try to compute SOABI from EXT_SUFFIX
-          list (GET _soabi 1 _values)
-          if (_values)
-            # clean-up: remove prefix character and suffix
-            string (REGEX REPLACE "^[.-](.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\1" _values "${_values}")
+        foreach (_item IN LISTS _soabi)
+          if (_item)
+            set (_values "${_item}")
+            break()
           endif()
+        endforeach()
+        if (_values)
+          # clean-up: remove prefix character and suffix
+          string (REGEX REPLACE "^[.-](.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\1" _values "${_values}")
         endif()
       endif()
     else()
@@ -364,7 +372,7 @@
       if (NAME STREQUAL "CONFIGDIR")
         set (config_flag "LIBPL")
       endif()
-      execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; from distutils import sysconfig; sys.stdout.write(sysconfig.get_config_var('${config_flag}'))"
+      execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\ntry:\n   from distutils import sysconfig\n   sys.stdout.write(sysconfig.get_config_var('${config_flag}'))\nexcept Exception:\n   import sysconfig\n   sys.stdout.write(sysconfig.get_config_var('${config_flag}'))"
                        RESULT_VARIABLE _result
                        OUTPUT_VARIABLE _values
                        ERROR_QUIET
@@ -392,6 +400,10 @@
     list (REMOVE_DUPLICATES _values)
   endif()
 
+  if (WIN32 AND NAME MATCHES "^(PREFIX|CONFIGDIR|INCLUDES)$")
+    file (TO_CMAKE_PATH "${_values}" _values)
+  endif()
+
   set (${_PYTHON_PGCV_VALUE} "${_values}" PARENT_SCOPE)
 endfunction()
 
@@ -1297,7 +1309,7 @@
 
       list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 1 ${_PYTHON_PREFIX}_VERSION_MAJOR)
       list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 2 ${_PYTHON_PREFIX}_VERSION_MINOR)
-      list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 3 ${_PYTHON_PREFIX}_VERSION_PATH)
+      list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 3 ${_PYTHON_PREFIX}_VERSION_PATCH)
 
       list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 4 _${_PYTHON_PREFIX}_ARCH)
       set (_${_PYTHON_PREFIX}_ARCH2 ${_${_PYTHON_PREFIX}_ARCH})
@@ -1381,7 +1393,7 @@
         endif()
 
         # retrieve various package installation directories
-        execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; from distutils import sysconfig;sys.stdout.write(';'.join([sysconfig.get_python_lib(plat_specific=False,standard_lib=True),sysconfig.get_python_lib(plat_specific=True,standard_lib=True),sysconfig.get_python_lib(plat_specific=False,standard_lib=False),sysconfig.get_python_lib(plat_specific=True,standard_lib=False)]))"
+        execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\ntry:\n   from distutils import sysconfig\n   sys.stdout.write(';'.join([sysconfig.get_python_lib(plat_specific=False,standard_lib=True),sysconfig.get_python_lib(plat_specific=True,standard_lib=True),sysconfig.get_python_lib(plat_specific=False,standard_lib=False),sysconfig.get_python_lib(plat_specific=True,standard_lib=False)]))\nexcept Exception:\n   import sysconfig\n   sys.stdout.write(';'.join([sysconfig.get_path('stdlib'),sysconfig.get_path('platstdlib'),sysconfig.get_path('purelib'),sysconfig.get_path('platlib')]))"
                         RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
                         OUTPUT_VARIABLE _${_PYTHON_PREFIX}_LIBPATHS
                         ERROR_QUIET)
diff --git a/Modules/GoogleTest.cmake b/Modules/GoogleTest.cmake
index 1d4398e..975ce6c 100644
--- a/Modules/GoogleTest.cmake
+++ b/Modules/GoogleTest.cmake
@@ -152,6 +152,7 @@
                          [TEST_LIST var]
                          [DISCOVERY_TIMEOUT seconds]
                          [XML_OUTPUT_DIR dir]
+                         [DISCOVERY_MODE <POST_BUILD|PRE_TEST>]
     )
 
   ``gtest_discover_tests`` sets up a post-build command on the test executable
@@ -244,6 +245,22 @@
     ``EXTRA_ARGS --gtest_output=xml`` to avoid race conditions writing the
     XML result output when using parallel test execution.
 
+  ``DISCOVERY_MODE``
+    Provides greater control over when ``gtest_discover_tests``performs test
+    discovery. By default, ``POST_BUILD`` sets up a post-build command
+    to perform test discovery at build time. In certain scenarios, like
+    cross-compiling, this ``POST_BUILD`` behavior is not desirable.
+    By contrast, ``PRE_TEST`` delays test discovery until just prior to test
+    execution. This way test discovery occurs in the target environment
+    where the test has a better chance at finding appropriate runtime
+    dependencies.
+
+    ``DISCOVERY_MODE`` defaults to the value of the
+    ``CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE`` variable if it is not
+    passed when calling ``gtest_discover_tests``. This provides a mechanism
+    for globally selecting a preferred test discovery behavior without having
+    to modify each call site.
+
 #]=======================================================================]
 
 # Save project's policies
@@ -376,11 +393,12 @@
 endfunction()
 
 #------------------------------------------------------------------------------
+
 function(gtest_discover_tests TARGET)
   cmake_parse_arguments(
     ""
     "NO_PRETTY_TYPES;NO_PRETTY_VALUES"
-    "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;DISCOVERY_TIMEOUT;XML_OUTPUT_DIR"
+    "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;DISCOVERY_TIMEOUT;XML_OUTPUT_DIR;DISCOVERY_MODE"
     "EXTRA_ARGS;PROPERTIES"
     ${ARGN}
   )
@@ -394,6 +412,12 @@
   if(NOT _DISCOVERY_TIMEOUT)
     set(_DISCOVERY_TIMEOUT 5)
   endif()
+  if(NOT _DISCOVERY_MODE)
+    if(NOT CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE)
+      set(CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE "POST_BUILD")
+    endif()
+    set(_DISCOVERY_MODE ${CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE})
+  endif()
 
   get_property(
     has_counter
@@ -425,35 +449,86 @@
     TARGET ${TARGET}
     PROPERTY CROSSCOMPILING_EMULATOR
   )
-  add_custom_command(
-    TARGET ${TARGET} POST_BUILD
-    BYPRODUCTS "${ctest_tests_file}"
-    COMMAND "${CMAKE_COMMAND}"
-            -D "TEST_TARGET=${TARGET}"
-            -D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
-            -D "TEST_EXECUTOR=${crosscompiling_emulator}"
-            -D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
-            -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
-            -D "TEST_PROPERTIES=${_PROPERTIES}"
-            -D "TEST_PREFIX=${_TEST_PREFIX}"
-            -D "TEST_SUFFIX=${_TEST_SUFFIX}"
-            -D "NO_PRETTY_TYPES=${_NO_PRETTY_TYPES}"
-            -D "NO_PRETTY_VALUES=${_NO_PRETTY_VALUES}"
-            -D "TEST_LIST=${_TEST_LIST}"
-            -D "CTEST_FILE=${ctest_tests_file}"
-            -D "TEST_DISCOVERY_TIMEOUT=${_DISCOVERY_TIMEOUT}"
-            -D "TEST_XML_OUTPUT_DIR=${_XML_OUTPUT_DIR}"
-            -P "${_GOOGLETEST_DISCOVER_TESTS_SCRIPT}"
-    VERBATIM
-  )
 
-  file(WRITE "${ctest_include_file}"
-    "if(EXISTS \"${ctest_tests_file}\")\n"
-    "  include(\"${ctest_tests_file}\")\n"
-    "else()\n"
-    "  add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)\n"
-    "endif()\n"
-  )
+  if(_DISCOVERY_MODE STREQUAL "POST_BUILD")
+    add_custom_command(
+      TARGET ${TARGET} POST_BUILD
+      BYPRODUCTS "${ctest_tests_file}"
+      COMMAND "${CMAKE_COMMAND}"
+              -D "TEST_TARGET=${TARGET}"
+              -D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
+              -D "TEST_EXECUTOR=${crosscompiling_emulator}"
+              -D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
+              -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
+              -D "TEST_PROPERTIES=${_PROPERTIES}"
+              -D "TEST_PREFIX=${_TEST_PREFIX}"
+              -D "TEST_SUFFIX=${_TEST_SUFFIX}"
+              -D "NO_PRETTY_TYPES=${_NO_PRETTY_TYPES}"
+              -D "NO_PRETTY_VALUES=${_NO_PRETTY_VALUES}"
+              -D "TEST_LIST=${_TEST_LIST}"
+              -D "CTEST_FILE=${ctest_tests_file}"
+              -D "TEST_DISCOVERY_TIMEOUT=${_DISCOVERY_TIMEOUT}"
+              -D "TEST_XML_OUTPUT_DIR=${_XML_OUTPUT_DIR}"
+              -P "${_GOOGLETEST_DISCOVER_TESTS_SCRIPT}"
+      VERBATIM
+    )
+
+    file(WRITE "${ctest_include_file}"
+      "if(EXISTS \"${ctest_tests_file}\")\n"
+      "  include(\"${ctest_tests_file}\")\n"
+      "else()\n"
+      "  add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)\n"
+      "endif()\n"
+    )
+  elseif(_DISCOVERY_MODE STREQUAL "PRE_TEST")
+
+    get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL
+        PROPERTY GENERATOR_IS_MULTI_CONFIG
+    )
+
+    if(GENERATOR_IS_MULTI_CONFIG)
+      set(ctest_tests_file "${ctest_file_base}_tests-$<CONFIG>.cmake")
+    endif()
+
+    string(CONCAT ctest_include_content
+      "if(EXISTS \"$<TARGET_FILE:${TARGET}>\")"                                    "\n"
+      "  if(\"$<TARGET_FILE:${TARGET}>\" IS_NEWER_THAN \"${ctest_tests_file}\")"   "\n"
+      "    include(GoogleTestAddTests)"                                            "\n"
+      "    gtest_discover_tests_impl("                                             "\n"
+      "      TEST_EXECUTABLE"        " [==[" "$<TARGET_FILE:${TARGET}>"   "]==]"   "\n"
+      "      TEST_EXECUTOR"          " [==[" "${crosscompiling_emulator}" "]==]"   "\n"
+      "      TEST_WORKING_DIR"       " [==[" "${_WORKING_DIRECTORY}"      "]==]"   "\n"
+      "      TEST_EXTRA_ARGS"        " [==[" "${_EXTRA_ARGS}"             "]==]"   "\n"
+      "      TEST_PROPERTIES"        " [==[" "${_PROPERTIES}"             "]==]"   "\n"
+      "      TEST_PREFIX"            " [==[" "${_TEST_PREFIX}"            "]==]"   "\n"
+      "      TEST_SUFFIX"            " [==[" "${_TEST_SUFFIX}"            "]==]"   "\n"
+      "      NO_PRETTY_TYPES"        " [==[" "${_NO_PRETTY_TYPES}"        "]==]"   "\n"
+      "      NO_PRETTY_VALUES"       " [==[" "${_NO_PRETTY_VALUES}"       "]==]"   "\n"
+      "      TEST_LIST"              " [==[" "${_TEST_LIST}"              "]==]"   "\n"
+      "      CTEST_FILE"             " [==[" "${ctest_tests_file}"        "]==]"   "\n"
+      "      TEST_DISCOVERY_TIMEOUT" " [==[" "${_DISCOVERY_TIMEOUT}"      "]==]"   "\n"
+      "      TEST_XML_OUTPUT_DIR"    " [==[" "${_XML_OUTPUT_DIR}"         "]==]"   "\n"
+      "    )"                                                                      "\n"
+      "  endif()"                                                                  "\n"
+      "  include(\"${ctest_tests_file}\")"                                         "\n"
+      "else()"                                                                     "\n"
+      "  add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)"                        "\n"
+      "endif()"                                                                    "\n"
+    )
+
+    if(GENERATOR_IS_MULTI_CONFIG)
+      foreach(_config ${CMAKE_CONFIGURATION_TYPES})
+        file(GENERATE OUTPUT "${ctest_file_base}_include-${_config}.cmake" CONTENT "${ctest_include_content}" CONDITION $<CONFIG:${_config}>)
+      endforeach()
+      file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include-\${CTEST_CONFIGURATION_TYPE}.cmake\")")
+    else()
+      file(GENERATE OUTPUT "${ctest_file_base}_include.cmake" CONTENT "${ctest_include_content}")
+      file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include.cmake\")")
+    endif()
+
+  else()
+    message(SEND_ERROR "Unknown DISCOVERY_MODE: ${_DISCOVERY_MODE}")
+  endif()
 
   # Add discovered tests to directory TEST_INCLUDE_FILES
   set_property(DIRECTORY
diff --git a/Modules/GoogleTestAddTests.cmake b/Modules/GoogleTestAddTests.cmake
index 753319f..65af4c2 100644
--- a/Modules/GoogleTestAddTests.cmake
+++ b/Modules/GoogleTestAddTests.cmake
@@ -3,21 +3,12 @@
 
 cmake_minimum_required(VERSION ${CMAKE_VERSION})
 
-set(prefix "${TEST_PREFIX}")
-set(suffix "${TEST_SUFFIX}")
-set(extra_args ${TEST_EXTRA_ARGS})
-set(properties ${TEST_PROPERTIES})
-set(script)
-set(suite)
-set(tests)
-set(tests_buffer)
-
-# Overwrite possibly existing ${CTEST_FILE} with empty file
+# Overwrite possibly existing ${_CTEST_FILE} with empty file
 set(flush_tests_MODE WRITE)
 
-# Flushes script to ${CTEST_FILE}
+# Flushes script to ${_CTEST_FILE}
 macro(flush_script)
-  file(${flush_tests_MODE} "${CTEST_FILE}" "${script}")
+  file(${flush_tests_MODE} "${_CTEST_FILE}" "${script}")
   set(flush_tests_MODE APPEND)
 
   set(script "")
@@ -48,98 +39,137 @@
   unset(_script_len)
 endmacro()
 
-# Run test executable to get list of available tests
-if(NOT EXISTS "${TEST_EXECUTABLE}")
-  message(FATAL_ERROR
-    "Specified test executable does not exist.\n"
-    "  Path: '${TEST_EXECUTABLE}'"
-  )
-endif()
-execute_process(
-  COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" --gtest_list_tests
-  WORKING_DIRECTORY "${TEST_WORKING_DIR}"
-  TIMEOUT ${TEST_DISCOVERY_TIMEOUT}
-  OUTPUT_VARIABLE output
-  RESULT_VARIABLE result
-)
-if(NOT ${result} EQUAL 0)
-  string(REPLACE "\n" "\n    " output "${output}")
-  message(FATAL_ERROR
-    "Error running test executable.\n"
-    "  Path: '${TEST_EXECUTABLE}'\n"
-    "  Result: ${result}\n"
-    "  Output:\n"
-    "    ${output}\n"
-  )
-endif()
+function(gtest_discover_tests_impl)
 
-string(REPLACE "\n" ";" output "${output}")
+  cmake_parse_arguments(
+    ""
+    ""
+    "NO_PRETTY_TYPES;NO_PRETTY_VALUES;TEST_EXECUTABLE;TEST_EXECUTOR;TEST_WORKING_DIR;TEST_PREFIX;TEST_SUFFIX;TEST_LIST;CTEST_FILE;TEST_DISCOVERY_TIMEOUT;TEST_XML_OUTPUT_DIR"
+    "TEST_EXTRA_ARGS;TEST_PROPERTIES"
+    ${ARGN}
+  )
 
-# Parse output
-foreach(line ${output})
-  # Skip header
-  if(NOT line MATCHES "gtest_main\\.cc")
-    # Do we have a module name or a test name?
-    if(NOT line MATCHES "^  ")
-      # Module; remove trailing '.' to get just the name...
-      string(REGEX REPLACE "\\.( *#.*)?" "" suite "${line}")
-      if(line MATCHES "#" AND NOT NO_PRETTY_TYPES)
-        string(REGEX REPLACE "/[0-9]\\.+ +#.*= +" "/" pretty_suite "${line}")
+  set(prefix "${_TEST_PREFIX}")
+  set(suffix "${_TEST_SUFFIX}")
+  set(extra_args ${_TEST_EXTRA_ARGS})
+  set(properties ${_TEST_PROPERTIES})
+  set(script)
+  set(suite)
+  set(tests)
+  set(tests_buffer)
+
+  # Run test executable to get list of available tests
+  if(NOT EXISTS "${_TEST_EXECUTABLE}")
+    message(FATAL_ERROR
+      "Specified test executable does not exist.\n"
+      "  Path: '${_TEST_EXECUTABLE}'"
+    )
+  endif()
+  execute_process(
+    COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" --gtest_list_tests
+    WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
+    TIMEOUT ${_TEST_DISCOVERY_TIMEOUT}
+    OUTPUT_VARIABLE output
+    RESULT_VARIABLE result
+  )
+  if(NOT ${result} EQUAL 0)
+    string(REPLACE "\n" "\n    " output "${output}")
+    message(FATAL_ERROR
+      "Error running test executable.\n"
+      "  Path: '${_TEST_EXECUTABLE}'\n"
+      "  Result: ${result}\n"
+      "  Output:\n"
+      "    ${output}\n"
+    )
+  endif()
+
+  string(REPLACE "\n" ";" output "${output}")
+
+  # Parse output
+  foreach(line ${output})
+    # Skip header
+    if(NOT line MATCHES "gtest_main\\.cc")
+      # Do we have a module name or a test name?
+      if(NOT line MATCHES "^  ")
+        # Module; remove trailing '.' to get just the name...
+        string(REGEX REPLACE "\\.( *#.*)?" "" suite "${line}")
+        if(line MATCHES "#" AND NOT _NO_PRETTY_TYPES)
+          string(REGEX REPLACE "/[0-9]\\.+ +#.*= +" "/" pretty_suite "${line}")
+        else()
+          set(pretty_suite "${suite}")
+        endif()
+        string(REGEX REPLACE "^DISABLED_" "" pretty_suite "${pretty_suite}")
       else()
-        set(pretty_suite "${suite}")
-      endif()
-      string(REGEX REPLACE "^DISABLED_" "" pretty_suite "${pretty_suite}")
-    else()
-      # Test name; strip spaces and comments to get just the name...
-      string(REGEX REPLACE " +" "" test "${line}")
-      if(test MATCHES "#" AND NOT NO_PRETTY_VALUES)
-        string(REGEX REPLACE "/[0-9]+#GetParam..=" "/" pretty_test "${test}")
-      else()
-        string(REGEX REPLACE "#.*" "" pretty_test "${test}")
-      endif()
-      string(REGEX REPLACE "^DISABLED_" "" pretty_test "${pretty_test}")
-      string(REGEX REPLACE "#.*" "" test "${test}")
-      if(NOT TEST_XML_OUTPUT_DIR STREQUAL "")
-        set(TEST_XML_OUTPUT_PARAM "--gtest_output=xml:${TEST_XML_OUTPUT_DIR}/${prefix}${pretty_suite}.${pretty_test}${suffix}.xml")
-      else()
-        unset(TEST_XML_OUTPUT_PARAM)
-      endif()
-      # ...and add to script
-      add_command(add_test
-        "${prefix}${pretty_suite}.${pretty_test}${suffix}"
-        ${TEST_EXECUTOR}
-        "${TEST_EXECUTABLE}"
-        "--gtest_filter=${suite}.${test}"
-        "--gtest_also_run_disabled_tests"
-        ${TEST_XML_OUTPUT_PARAM}
-        ${extra_args}
-      )
-      if(suite MATCHES "^DISABLED" OR test MATCHES "^DISABLED")
+        # Test name; strip spaces and comments to get just the name...
+        string(REGEX REPLACE " +" "" test "${line}")
+        if(test MATCHES "#" AND NOT _NO_PRETTY_VALUES)
+          string(REGEX REPLACE "/[0-9]+#GetParam..=" "/" pretty_test "${test}")
+        else()
+          string(REGEX REPLACE "#.*" "" pretty_test "${test}")
+        endif()
+        string(REGEX REPLACE "^DISABLED_" "" pretty_test "${pretty_test}")
+        string(REGEX REPLACE "#.*" "" test "${test}")
+        if(NOT "${_TEST_XML_OUTPUT_DIR}" STREQUAL "")
+          set(TEST_XML_OUTPUT_PARAM "--gtest_output=xml:${_TEST_XML_OUTPUT_DIR}/${prefix}${pretty_suite}.${pretty_test}${suffix}.xml")
+        else()
+          unset(TEST_XML_OUTPUT_PARAM)
+        endif()
+        # ...and add to script
+        add_command(add_test
+          "${prefix}${pretty_suite}.${pretty_test}${suffix}"
+          ${_TEST_EXECUTOR}
+          "${_TEST_EXECUTABLE}"
+          "--gtest_filter=${suite}.${test}"
+          "--gtest_also_run_disabled_tests"
+          ${TEST_XML_OUTPUT_PARAM}
+          ${extra_args}
+        )
+        if(suite MATCHES "^DISABLED" OR test MATCHES "^DISABLED")
+          add_command(set_tests_properties
+            "${prefix}${pretty_suite}.${pretty_test}${suffix}"
+            PROPERTIES DISABLED TRUE
+          )
+        endif()
         add_command(set_tests_properties
           "${prefix}${pretty_suite}.${pretty_test}${suffix}"
-          PROPERTIES DISABLED TRUE
+          PROPERTIES
+          WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
+          ${properties}
         )
-      endif()
-      add_command(set_tests_properties
-        "${prefix}${pretty_suite}.${pretty_test}${suffix}"
-        PROPERTIES
-        WORKING_DIRECTORY "${TEST_WORKING_DIR}"
-        ${properties}
-      )
-      list(APPEND tests_buffer "${prefix}${pretty_suite}.${pretty_test}${suffix}")
-      list(LENGTH tests_buffer tests_buffer_length)
-      if(${tests_buffer_length} GREATER "250")
-        flush_tests_buffer()
+        list(APPEND tests_buffer "${prefix}${pretty_suite}.${pretty_test}${suffix}")
+        list(LENGTH tests_buffer tests_buffer_length)
+        if(${tests_buffer_length} GREATER "250")
+          flush_tests_buffer()
+        endif()
       endif()
     endif()
-  endif()
-endforeach()
+  endforeach()
 
 
-# Create a list of all discovered tests, which users may use to e.g. set
-# properties on the tests
-flush_tests_buffer()
-add_command(set ${TEST_LIST} ${tests})
+  # Create a list of all discovered tests, which users may use to e.g. set
+  # properties on the tests
+  flush_tests_buffer()
+  add_command(set ${_TEST_LIST} ${tests})
 
-# Write CTest script
-flush_script()
+  # Write CTest script
+  flush_script()
+
+endfunction()
+
+if(CMAKE_SCRIPT_MODE_FILE)
+  gtest_discover_tests_impl(
+    NO_PRETTY_TYPES ${NO_PRETTY_TYPES}
+    NO_PRETTY_VALUES ${NO_PRETTY_VALUES}
+    TEST_EXECUTABLE ${TEST_EXECUTABLE}
+    TEST_EXECUTOR ${TEST_EXECUTOR}
+    TEST_WORKING_DIR ${TEST_WORKING_DIR}
+    TEST_PREFIX ${TEST_PREFIX}
+    TEST_SUFFIX ${TEST_SUFFIX}
+    TEST_LIST ${TEST_LIST}
+    CTEST_FILE ${CTEST_FILE}
+    TEST_DISCOVERY_TIMEOUT ${TEST_DISCOVERY_TIMEOUT}
+    TEST_XML_OUTPUT_DIR ${TEST_XML_OUTPUT_DIR}
+    TEST_EXTRA_ARGS ${TEST_EXTRA_ARGS}
+    TEST_PROPERTIES ${TEST_PROPERTIES}
+  )
+endif()
diff --git a/Modules/Platform/Windows-Clang.cmake b/Modules/Platform/Windows-Clang.cmake
index 0c366b4..018de4c 100644
--- a/Modules/Platform/Windows-Clang.cmake
+++ b/Modules/Platform/Windows-Clang.cmake
@@ -134,14 +134,15 @@
     include(Platform/Windows-MSVC)
 
     # Feed the preprocessed rc file to llvm-rc
-    if(CMAKE_RC_COMPILER_INIT STREQUAL "llvm-rc")
-      if(DEFINED CMAKE_C_COMPILER)
+    if(CMAKE_RC_COMPILER_INIT MATCHES "llvm-rc")
+      if(DEFINED CMAKE_C_COMPILER_ID)
         set(CMAKE_RC_PREPROCESSOR CMAKE_C_COMPILER)
-      elseif(DEFINED CMAKE_CXX_COMPILER)
+      elseif(DEFINED CMAKE_CXX_COMPILER_ID)
         set(CMAKE_RC_PREPROCESSOR CMAKE_CXX_COMPILER)
       endif()
       if(DEFINED CMAKE_RC_PREPROCESSOR)
-        set(CMAKE_RC_COMPILE_OBJECT "${CMAKE_COMMAND} -E cmake_llvm_rc <OBJECT>.pp <${CMAKE_RC_PREPROCESSOR}> <DEFINES> -DRC_INVOKED <INCLUDES> <FLAGS> -clang:-MD -clang:-MF -clang:<SOURCE>.d -E <SOURCE> -- <CMAKE_RC_COMPILER> <DEFINES> /fo <OBJECT> <OBJECT>.pp")
+        set(CMAKE_DEPFILE_FLAGS_RC "-clang:-MD -clang:-MF -clang:<DEPFILE>")
+        set(CMAKE_RC_COMPILE_OBJECT "${CMAKE_COMMAND} -E cmake_llvm_rc <OBJECT>.pp <${CMAKE_RC_PREPROCESSOR}> <DEFINES> -DRC_INVOKED <INCLUDES> <FLAGS> -E <SOURCE> -- <CMAKE_RC_COMPILER> <DEFINES> /fo <OBJECT> <OBJECT>.pp")
         if(CMAKE_GENERATOR STREQUAL "Ninja")
           set(CMAKE_NINJA_CMCLDEPS_RC 0)
           set(CMAKE_NINJA_DEP_TYPE_RC gcc)
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 73b7771..b37e1ee 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 3)
 set(CMake_VERSION_MINOR 17)
-set(CMake_VERSION_PATCH 20200326)
+set(CMake_VERSION_PATCH 20200331)
 #set(CMake_VERSION_RC 0)
 set(CMake_VERSION_IS_DIRTY 0)
 
diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx
index d867fa1..811a7e8 100644
--- a/Source/CTest/cmCTestSubmitHandler.cxx
+++ b/Source/CTest/cmCTestSubmitHandler.cxx
@@ -260,11 +260,10 @@
         cmCTestScriptHandler* ch = this->CTest->GetScriptHandler();
         cmake* cm = ch->GetCMake();
         if (cm) {
-          const char* subproject =
-            cm->GetState()->GetGlobalProperty("SubProject");
+          cmProp subproject = cm->GetState()->GetGlobalProperty("SubProject");
           if (subproject) {
             upload_as += "&subproject=";
-            upload_as += ctest_curl.Escape(subproject);
+            upload_as += ctest_curl.Escape(*subproject);
           }
         }
       }
@@ -556,11 +555,11 @@
   // a "&subproject=subprojectname" to the first POST.
   cmCTestScriptHandler* ch = this->CTest->GetScriptHandler();
   cmake* cm = ch->GetCMake();
-  const char* subproject = cm->GetState()->GetGlobalProperty("SubProject");
+  cmProp subproject = cm->GetState()->GetGlobalProperty("SubProject");
   // TODO: Encode values for a URL instead of trusting caller.
   std::ostringstream str;
   if (subproject) {
-    str << "subproject=" << curl.Escape(subproject) << "&";
+    str << "subproject=" << curl.Escape(*subproject) << "&";
   }
   auto timeNow =
     std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
diff --git a/Source/CTest/cmProcess.cxx b/Source/CTest/cmProcess.cxx
index 76ffb20..2742fd7 100644
--- a/Source/CTest/cmProcess.cxx
+++ b/Source/CTest/cmProcess.cxx
@@ -249,7 +249,7 @@
   this->PipeReader.reset();
   if (this->ProcessHandleClosed) {
     uv_timer_stop(this->Timer);
-    this->Runner->FinalizeTest();
+    this->Finish();
   }
 }
 
@@ -291,7 +291,7 @@
     // Our on-exit handler already ran but did not finish the test
     // because we were still reading output.  We've just dropped
     // our read handler, so we need to finish the test now.
-    this->Runner->FinalizeTest();
+    this->Finish();
   }
 }
 
@@ -321,6 +321,16 @@
   // Record exit information.
   this->ExitValue = exit_status;
   this->Signal = term_signal;
+
+  this->ProcessHandleClosed = true;
+  if (this->ReadHandleClosed) {
+    uv_timer_stop(this->Timer);
+    this->Finish();
+  }
+}
+
+void cmProcess::Finish()
+{
   this->TotalTime = std::chrono::steady_clock::now() - this->StartTime;
   // Because of a processor clock scew the runtime may become slightly
   // negative. If someone changed the system clock while the process was
@@ -329,12 +339,7 @@
   if (this->TotalTime <= cmDuration::zero()) {
     this->TotalTime = cmDuration::zero();
   }
-
-  this->ProcessHandleClosed = true;
-  if (this->ReadHandleClosed) {
-    uv_timer_stop(this->Timer);
-    this->Runner->FinalizeTest();
-  }
+  this->Runner->FinalizeTest();
 }
 
 cmProcess::State cmProcess::GetProcessStatus()
diff --git a/Source/CTest/cmProcess.h b/Source/CTest/cmProcess.h
index 0f69f68..526c920 100644
--- a/Source/CTest/cmProcess.h
+++ b/Source/CTest/cmProcess.h
@@ -108,6 +108,7 @@
   void OnAllocate(size_t suggested_size, uv_buf_t* buf);
 
   void StartTimer();
+  void Finish();
 
   class Buffer : public std::vector<char>
   {
diff --git a/Source/CursesDialog/cmCursesCacheEntryComposite.cxx b/Source/CursesDialog/cmCursesCacheEntryComposite.cxx
index 9a0d966..9250fbc 100644
--- a/Source/CursesDialog/cmCursesCacheEntryComposite.cxx
+++ b/Source/CursesDialog/cmCursesCacheEntryComposite.cxx
@@ -71,11 +71,11 @@
       break;
     }
     case cmStateEnums::STRING: {
-      const char* stringsProp = state->GetCacheEntryProperty(key, "STRINGS");
+      cmProp stringsProp = state->GetCacheEntryProperty(key, "STRINGS");
       if (stringsProp) {
         auto ow =
           cm::make_unique<cmCursesOptionsWidget>(this->EntryWidth, 1, 1, 1);
-        for (std::string const& opt : cmExpandedList(stringsProp)) {
+        for (std::string const& opt : cmExpandedList(*stringsProp)) {
           ow->AddOption(opt);
         }
         ow->SetOption(*value);
diff --git a/Source/CursesDialog/cmCursesMainForm.cxx b/Source/CursesDialog/cmCursesMainForm.cxx
index 9670595..7752a68 100644
--- a/Source/CursesDialog/cmCursesMainForm.cxx
+++ b/Source/CursesDialog/cmCursesMainForm.cxx
@@ -407,9 +407,10 @@
     auto cmakeState = this->CMakeInstance->GetState();
     cmProp existingValue = cmakeState->GetCacheEntryValue(labelValue);
     if (existingValue) {
-      auto help = cmakeState->GetCacheEntryProperty(labelValue, "HELPSTRING");
+      cmProp help =
+        cmakeState->GetCacheEntryProperty(labelValue, "HELPSTRING");
       if (help) {
-        bar += help;
+        bar += *help;
       }
     }
   }
@@ -802,7 +803,7 @@
         cmCursesWidget* lbl = reinterpret_cast<cmCursesWidget*>(
           field_userptr(this->Fields[findex - 2]));
         const char* curField = lbl->GetValue();
-        const char* helpString = nullptr;
+        cmProp helpString = nullptr;
 
         cmProp existingValue =
           this->CMakeInstance->GetState()->GetCacheEntryValue(curField);
@@ -813,7 +814,7 @@
         if (helpString) {
           this->HelpMessage[1] =
             cmStrCat("Current option is: ", curField, '\n',
-                     "Help string for this option is: ", helpString, '\n');
+                     "Help string for this option is: ", *helpString, '\n');
         } else {
           this->HelpMessage[1] = "";
         }
diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx
index 2543215..776af81 100644
--- a/Source/QtDialog/QCMake.cxx
+++ b/Source/QtDialog/QCMake.cxx
@@ -306,8 +306,9 @@
 
     QCMakeProperty prop;
     prop.Key = QString::fromLocal8Bit(key.c_str());
-    prop.Help =
-      QString::fromLocal8Bit(state->GetCacheEntryProperty(key, "HELPSTRING"));
+    if (cmProp hs = state->GetCacheEntryProperty(key, "HELPSTRING")) {
+      prop.Help = QString::fromLocal8Bit(hs->c_str());
+    }
     prop.Value = QString::fromLocal8Bit(cachedValue->c_str());
     prop.Advanced = state->GetCacheEntryPropertyAsBool(key, "ADVANCED");
     if (t == cmStateEnums::BOOL) {
@@ -319,10 +320,10 @@
       prop.Type = QCMakeProperty::FILEPATH;
     } else if (t == cmStateEnums::STRING) {
       prop.Type = QCMakeProperty::STRING;
-      const char* stringsProperty =
-        state->GetCacheEntryProperty(key, "STRINGS");
+      cmProp stringsProperty = state->GetCacheEntryProperty(key, "STRINGS");
       if (stringsProperty) {
-        prop.Strings = QString::fromLocal8Bit(stringsProperty).split(";");
+        prop.Strings =
+          QString::fromLocal8Bit(stringsProperty->c_str()).split(";");
       }
     }
 
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx
index b9fb4a0..c5505f9 100644
--- a/Source/cmCTest.cxx
+++ b/Source/cmCTest.cxx
@@ -1443,16 +1443,15 @@
     return;
   }
   // This code should go when cdash is changed to use labels only
-  const char* subproject = cm->GetState()->GetGlobalProperty("SubProject");
+  cmProp subproject = cm->GetState()->GetGlobalProperty("SubProject");
   if (subproject) {
     xml.StartElement("Subproject");
-    xml.Attribute("name", subproject);
-    const char* labels =
+    xml.Attribute("name", *subproject);
+    cmProp labels =
       ch->GetCMake()->GetState()->GetGlobalProperty("SubProjectLabels");
     if (labels) {
       xml.StartElement("Labels");
-      std::string l = labels;
-      std::vector<std::string> args = cmExpandedList(l);
+      std::vector<std::string> args = cmExpandedList(*labels);
       for (std::string const& i : args) {
         xml.Element("Label", i);
       }
@@ -1462,10 +1461,10 @@
   }
 
   // This code should stay when cdash only does label based sub-projects
-  const char* label = cm->GetState()->GetGlobalProperty("Label");
+  cmProp label = cm->GetState()->GetGlobalProperty("Label");
   if (label) {
     xml.StartElement("Labels");
-    xml.Element("Label", label);
+    xml.Element("Label", *label);
     xml.EndElement();
   }
 }
diff --git a/Source/cmCacheManager.cxx b/Source/cmCacheManager.cxx
index c444fce..31a8692 100644
--- a/Source/cmCacheManager.cxx
+++ b/Source/cmCacheManager.cxx
@@ -77,7 +77,7 @@
     }
     while (realbuffer[0] == '/' && realbuffer[1] == '/') {
       if ((realbuffer[2] == '\\') && (realbuffer[3] == 'n')) {
-        helpString += "\n";
+        helpString += '\n';
         helpString += &realbuffer[4];
       } else {
         helpString += &realbuffer[2];
@@ -117,8 +117,8 @@
       }
     } else {
       std::ostringstream error;
-      error << "Parse error in cache file " << cacheFile;
-      error << " on line " << lineno << ". Offending entry: " << realbuffer;
+      error << "Parse error in cache file " << cacheFile << " on line "
+            << lineno << ". Offending entry: " << realbuffer;
       cmSystemTools::Error(error.str());
     }
   }
@@ -217,7 +217,7 @@
       cmCacheManager::OutputKey(os, key);
       os << ":INTERNAL=";
       cmCacheManager::OutputValue(os, *value);
-      os << "\n";
+      os << '\n';
       cmCacheManager::OutputNewlineTruncationWarning(os, key, *value,
                                                      messenger);
     }
@@ -268,31 +268,29 @@
 
   /* clang-format off */
   fout << "# This is the CMakeCache file.\n"
-       << "# For build in directory: " << currentcwd << "\n"
-       << "# It was generated by CMake: "
-       << cmSystemTools::GetCMakeCommand() << std::endl;
+          "# For build in directory: " << currentcwd << "\n"
+          "# It was generated by CMake: "
+       << cmSystemTools::GetCMakeCommand()
+       << "\n"
+          "# You can edit this file to change values found and used by cmake."
+          "\n"
+          "# If you do not want to change any of the values, simply exit the "
+          "editor.\n"
+          "# If you do want to change a value, simply edit, save, and exit "
+          "the editor.\n"
+          "# The syntax for the file is as follows:\n"
+          "# KEY:TYPE=VALUE\n"
+          "# KEY is the name of a variable in the cache.\n"
+          "# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!."
+          "\n"
+          "# VALUE is the current value for the KEY.\n"
+          "\n"
+          "########################\n"
+          "# EXTERNAL cache entries\n"
+          "########################\n"
+          "\n";
   /* clang-format on */
 
-  /* clang-format off */
-  fout << "# You can edit this file to change values found and used by cmake."
-       << std::endl
-       << "# If you do not want to change any of the values, simply exit the "
-       "editor." << std::endl
-       << "# If you do want to change a value, simply edit, save, and exit "
-       "the editor." << std::endl
-       << "# The syntax for the file is as follows:\n"
-       << "# KEY:TYPE=VALUE\n"
-       << "# KEY is the name of a variable in the cache.\n"
-       << "# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT "
-       "TYPE!." << std::endl
-       << "# VALUE is the current value for the KEY.\n\n";
-  /* clang-format on */
-
-  fout << "########################\n";
-  fout << "# EXTERNAL cache entries\n";
-  fout << "########################\n";
-  fout << "\n";
-
   for (auto const& i : this->Cache) {
     CacheEntry const& ce = i.second;
     cmStateEnums::CacheEntryType t = ce.Type;
@@ -309,20 +307,20 @@
         cmCacheManager::OutputHelpString(fout, "Missing description");
       }
       cmCacheManager::OutputKey(fout, i.first);
-      fout << ":" << cmState::CacheEntryTypeToString(t) << "=";
+      fout << ':' << cmState::CacheEntryTypeToString(t) << '=';
       cmCacheManager::OutputValue(fout, ce.Value);
-      fout << "\n";
+      fout << '\n';
       cmCacheManager::OutputNewlineTruncationWarning(fout, i.first, ce.Value,
                                                      messenger);
-      fout << "\n";
+      fout << '\n';
     }
   }
 
-  fout << "\n";
-  fout << "########################\n";
-  fout << "# INTERNAL cache entries\n";
-  fout << "########################\n";
-  fout << "\n";
+  fout << "\n"
+          "########################\n"
+          "# INTERNAL cache entries\n"
+          "########################\n"
+          "\n";
 
   for (cmCacheManager::CacheIterator i = this->NewIterator(); !i.IsAtEnd();
        i.Next()) {
@@ -338,14 +336,14 @@
         cmCacheManager::OutputHelpString(fout, *help);
       }
       cmCacheManager::OutputKey(fout, i.GetName());
-      fout << ":" << cmState::CacheEntryTypeToString(t) << "=";
+      fout << ':' << cmState::CacheEntryTypeToString(t) << '=';
       cmCacheManager::OutputValue(fout, i.GetValue());
-      fout << "\n";
+      fout << '\n';
       cmCacheManager::OutputNewlineTruncationWarning(fout, i.GetName(),
                                                      i.GetValue(), messenger);
     }
   }
-  fout << "\n";
+  fout << '\n';
   fout.Close();
   std::string checkCacheFile = cmStrCat(path, "/CMakeFiles");
   cmSystemTools::MakeDirectory(checkCacheFile);
@@ -430,7 +428,7 @@
         fout << "\\n";
       }
       oneLine = helpString.substr(pos, i - pos);
-      fout << oneLine << "\n";
+      fout << oneLine << '\n';
       pos = i;
     }
   }
@@ -452,7 +450,7 @@
         fout << "\\n";
       }
       oneLine = message.substr(pos, i - pos);
-      fout << oneLine << "\n";
+      fout << oneLine << '\n';
       pos = i;
     }
   }
@@ -519,17 +517,17 @@
 
 void cmCacheManager::PrintCache(std::ostream& out) const
 {
-  out << "=================================================" << std::endl;
-  out << "CMakeCache Contents:" << std::endl;
+  out << "=================================================\n"
+         "CMakeCache Contents:\n";
   for (auto const& i : this->Cache) {
     if (i.second.Type != cmStateEnums::INTERNAL) {
-      out << i.first << " = " << i.second.Value << std::endl;
+      out << i.first << " = " << i.second.Value << '\n';
     }
   }
-  out << "\n\n";
-  out << "To change values in the CMakeCache, " << std::endl
-      << "edit CMakeCache.txt in your output directory.\n";
-  out << "=================================================" << std::endl;
+  out << "\n\n"
+         "To change values in the CMakeCache, \n"
+         "edit CMakeCache.txt in your output directory.\n"
+         "=================================================\n";
 }
 
 void cmCacheManager::AddCacheEntry(const std::string& key, const char* value,
diff --git a/Source/cmCallVisualStudioMacro.cxx b/Source/cmCallVisualStudioMacro.cxx
index 9e152ff..94b6e18 100644
--- a/Source/cmCallVisualStudioMacro.cxx
+++ b/Source/cmCallVisualStudioMacro.cxx
@@ -43,8 +43,7 @@
       if (LogErrorsAsMessages) {                                              \
         std::ostringstream _hresult_oss;                                      \
         _hresult_oss.flags(std::ios::hex);                                    \
-        _hresult_oss << context << " failed HRESULT, hr = 0x" << hr           \
-                     << std::endl;                                            \
+        _hresult_oss << context << " failed HRESULT, hr = 0x" << hr << '\n';  \
         _hresult_oss.flags(std::ios::dec);                                    \
         _hresult_oss << __FILE__ << "(" << __LINE__ << ")";                   \
         cmSystemTools::Message(_hresult_oss.str());                           \
@@ -98,32 +97,37 @@
                          DISPATCH_METHOD, &params, &result, &excep, &arg);
 
       std::ostringstream oss;
-      oss << std::endl;
-      oss << "Invoke(ExecuteCommand)" << std::endl;
-      oss << "  Macro: " << macro << std::endl;
-      oss << "  Args: " << args << std::endl;
+      /* clang-format off */
+      oss << "\nInvoke(ExecuteCommand)\n"
+             "  Macro: " << macro << "\n"
+             "  Args: " << args << '\n';
+      /* clang-format on */
 
       if (DISP_E_EXCEPTION == hr) {
-        oss << "DISP_E_EXCEPTION EXCEPINFO:" << excep.wCode << std::endl;
-        oss << "  wCode: " << excep.wCode << std::endl;
-        oss << "  wReserved: " << excep.wReserved << std::endl;
+        /* clang-format off */
+        oss << "DISP_E_EXCEPTION EXCEPINFO:" << excep.wCode << "\n"
+               "  wCode: " << excep.wCode << "\n"
+               "  wReserved: " << excep.wReserved << '\n';
+        /* clang-format on */
         if (excep.bstrSource) {
           oss << "  bstrSource: " << (const char*)(_bstr_t)excep.bstrSource
-              << std::endl;
+              << '\n';
         }
         if (excep.bstrDescription) {
           oss << "  bstrDescription: "
-              << (const char*)(_bstr_t)excep.bstrDescription << std::endl;
+              << (const char*)(_bstr_t)excep.bstrDescription << '\n';
         }
         if (excep.bstrHelpFile) {
           oss << "  bstrHelpFile: " << (const char*)(_bstr_t)excep.bstrHelpFile
-              << std::endl;
+              << '\n';
         }
-        oss << "  dwHelpContext: " << excep.dwHelpContext << std::endl;
-        oss << "  pvReserved: " << excep.pvReserved << std::endl;
-        oss << "  pfnDeferredFillIn: "
-            << reinterpret_cast<void*>(excep.pfnDeferredFillIn) << std::endl;
-        oss << "  scode: " << excep.scode << std::endl;
+        /* clang-format off */
+        oss << "  dwHelpContext: " << excep.dwHelpContext << "\n"
+               "  pvReserved: " << excep.pvReserved << "\n"
+               "  pfnDeferredFillIn: "
+            << reinterpret_cast<void*>(excep.pfnDeferredFillIn) << "\n"
+               "  scode: " << excep.scode << '\n';
+        /* clang-format on */
       }
 
       std::string exstr(oss.str());
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 @@
     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/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index 003019a..ea31417 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -276,8 +276,7 @@
                 << "\"\nhowever it is also "
                    "a subdirectory of the "
                 << (inBinary ? "build" : "source") << " tree:\n    \""
-                << (inBinary ? topBinaryDir : topSourceDir) << "\""
-                << std::endl;
+                << (inBinary ? topBinaryDir : topSourceDir) << "\"\n";
               target->GetLocalGenerator()->IssueMessage(
                 MessageType::AUTHOR_WARNING, s.str());
               CM_FALLTHROUGH;
diff --git a/Source/cmExtraEclipseCDT4Generator.cxx b/Source/cmExtraEclipseCDT4Generator.cxx
index aea3dc4..737b0e5 100644
--- a/Source/cmExtraEclipseCDT4Generator.cxx
+++ b/Source/cmExtraEclipseCDT4Generator.cxx
@@ -415,9 +415,9 @@
     xml.Element("nature", n);
   }
 
-  if (const char* extraNaturesProp =
+  if (cmProp extraNaturesProp =
         mf->GetState()->GetGlobalProperty("ECLIPSE_EXTRA_NATURES")) {
-    std::vector<std::string> extraNatures = cmExpandedList(extraNaturesProp);
+    std::vector<std::string> extraNatures = cmExpandedList(*extraNaturesProp);
     for (std::string const& n : extraNatures) {
       xml.Element("nature", n);
     }
@@ -1033,9 +1033,9 @@
   xml.EndElement(); // storageModule
 
   // Append additional cproject contents without applying any XML formatting
-  if (const char* extraCProjectContents =
+  if (cmProp extraCProjectContents =
         mf->GetState()->GetGlobalProperty("ECLIPSE_EXTRA_CPROJECT_CONTENTS")) {
-    fout << extraCProjectContents;
+    fout << *extraCProjectContents;
   }
 
   xml.EndElement(); // cproject
diff --git a/Source/cmFileAPICache.cxx b/Source/cmFileAPICache.cxx
index 639df52..5d2ddf9 100644
--- a/Source/cmFileAPICache.cxx
+++ b/Source/cmFileAPICache.cxx
@@ -94,7 +94,8 @@
 {
   Json::Value property = Json::objectValue;
   property["name"] = prop;
-  property["value"] = this->State->GetCacheEntryProperty(name, prop);
+  cmProp p = this->State->GetCacheEntryProperty(name, prop);
+  property["value"] = p ? *p : "";
   return property;
 }
 }
diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx
index 4d627bd..b5553b8 100644
--- a/Source/cmFindBase.cxx
+++ b/Source/cmFindBase.cxx
@@ -312,9 +312,9 @@
       return true;
     }
     if (cached) {
-      const char* hs =
+      cmProp hs =
         state->GetCacheEntryProperty(this->VariableName, "HELPSTRING");
-      this->VariableDocumentation = hs ? hs : "(none)";
+      this->VariableDocumentation = hs ? *hs : "(none)";
     }
   }
   return false;
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index f606002..e996327 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -1114,12 +1114,10 @@
 void cmFindPackageCommand::AppendToFoundProperty(bool found)
 {
   std::vector<std::string> foundContents;
-  const char* foundProp =
+  cmProp foundProp =
     this->Makefile->GetState()->GetGlobalProperty("PACKAGES_FOUND");
-  if (foundProp && *foundProp) {
-    std::string tmp = foundProp;
-
-    cmExpandList(tmp, foundContents, false);
+  if (foundProp && !foundProp->empty()) {
+    cmExpandList(*foundProp, foundContents, false);
     auto nameIt =
       std::find(foundContents.begin(), foundContents.end(), this->Name);
     if (nameIt != foundContents.end()) {
@@ -1128,12 +1126,10 @@
   }
 
   std::vector<std::string> notFoundContents;
-  const char* notFoundProp =
+  cmProp notFoundProp =
     this->Makefile->GetState()->GetGlobalProperty("PACKAGES_NOT_FOUND");
-  if (notFoundProp && *notFoundProp) {
-    std::string tmp = notFoundProp;
-
-    cmExpandList(tmp, notFoundContents, false);
+  if (notFoundProp && !notFoundProp->empty()) {
+    cmExpandList(*notFoundProp, notFoundContents, false);
     auto nameIt =
       std::find(notFoundContents.begin(), notFoundContents.end(), this->Name);
     if (nameIt != notFoundContents.end()) {
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index af95089..e722034 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -3781,9 +3781,16 @@
   if (inserted.second) {
     std::string& createOptionList = inserted.first->second;
 
+    if (this->GetPropertyAsBool("PCH_WARN_INVALID")) {
+      createOptionList = this->Makefile->GetSafeDefinition(
+        cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_INVALID_PCH"));
+    }
+
     const std::string createOptVar =
       cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_CREATE_PCH");
-    createOptionList = this->Makefile->GetSafeDefinition(createOptVar);
+
+    createOptionList = cmStrCat(
+      createOptionList, ";", this->Makefile->GetSafeDefinition(createOptVar));
 
     const std::string pchHeader = this->GetPchHeader(config, language);
     const std::string pchFile = this->GetPchFile(config, language);
@@ -3802,9 +3809,16 @@
   if (inserted.second) {
     std::string& useOptionList = inserted.first->second;
 
+    if (this->GetPropertyAsBool("PCH_WARN_INVALID")) {
+      useOptionList = this->Makefile->GetSafeDefinition(
+        cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_INVALID_PCH"));
+    }
+
     const std::string useOptVar =
       cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_USE_PCH");
-    useOptionList = this->Makefile->GetSafeDefinition(useOptVar);
+
+    useOptionList = cmStrCat(useOptionList, ";",
+                             this->Makefile->GetSafeDefinition(useOptVar));
 
     const std::string pchHeader = this->GetPchHeader(config, language);
     const std::string pchFile = this->GetPchFile(config, language);
diff --git a/Source/cmGetCMakePropertyCommand.cxx b/Source/cmGetCMakePropertyCommand.cxx
index ff4e312..4a9509b 100644
--- a/Source/cmGetCMakePropertyCommand.cxx
+++ b/Source/cmGetCMakePropertyCommand.cxx
@@ -36,12 +36,12 @@
       status.GetMakefile().GetGlobalGenerator()->GetInstallComponents();
     output = cmJoin(*components, ";");
   } else {
-    const char* prop = nullptr;
+    cmProp prop = nullptr;
     if (!args[1].empty()) {
       prop = status.GetMakefile().GetState()->GetGlobalProperty(args[1]);
     }
     if (prop) {
-      output = prop;
+      output = *prop;
     }
   }
 
diff --git a/Source/cmGetPropertyCommand.cxx b/Source/cmGetPropertyCommand.cxx
index 2c3adde..1d8f0a9 100644
--- a/Source/cmGetPropertyCommand.cxx
+++ b/Source/cmGetPropertyCommand.cxx
@@ -241,8 +241,9 @@
 
   // Get the property.
   cmake* cm = status.GetMakefile().GetCMakeInstance();
+  cmProp p = cm->GetState()->GetGlobalProperty(propertyName);
   return StoreResult(infoType, status.GetMakefile(), variable,
-                     cm->GetState()->GetGlobalProperty(propertyName));
+                     p ? p->c_str() : nullptr);
 }
 
 bool HandleDirectoryMode(cmExecutionStatus& status, const std::string& name,
@@ -387,12 +388,13 @@
     return false;
   }
 
-  const char* value = nullptr;
+  cmProp value = nullptr;
   if (status.GetMakefile().GetState()->GetCacheEntryValue(name)) {
     value = status.GetMakefile().GetState()->GetCacheEntryProperty(
       name, propertyName);
   }
-  StoreResult(infoType, status.GetMakefile(), variable, value);
+  StoreResult(infoType, status.GetMakefile(), variable,
+              value ? value->c_str() : nullptr);
   return true;
 }
 
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index 0e10734..6d08369 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -246,11 +246,10 @@
     cmSystemTools::ConvertToUnixSlashes(cnameString);
     cmSystemTools::ConvertToUnixSlashes(pathString);
     if (cnameString != pathString) {
-      const char* cvars =
-        this->GetCMakeInstance()->GetState()->GetGlobalProperty(
-          "__CMAKE_DELETE_CACHE_CHANGE_VARS_");
+      cmProp cvars = this->GetCMakeInstance()->GetState()->GetGlobalProperty(
+        "__CMAKE_DELETE_CACHE_CHANGE_VARS_");
       if (cvars) {
-        changeVars += cvars;
+        changeVars += *cvars;
         changeVars += ";";
       }
       changeVars += langComp;
@@ -2674,13 +2673,13 @@
   }
 }
 
-const char* cmGlobalGenerator::GetPredefinedTargetsFolder()
+std::string cmGlobalGenerator::GetPredefinedTargetsFolder()
 {
-  const char* prop = this->GetCMakeInstance()->GetState()->GetGlobalProperty(
+  cmProp prop = this->GetCMakeInstance()->GetState()->GetGlobalProperty(
     "PREDEFINED_TARGETS_FOLDER");
 
   if (prop) {
-    return prop;
+    return *prop;
   }
 
   return "CMakePredefinedTargets";
@@ -2688,13 +2687,13 @@
 
 bool cmGlobalGenerator::UseFolderProperty() const
 {
-  const char* prop =
+  cmProp prop =
     this->GetCMakeInstance()->GetState()->GetGlobalProperty("USE_FOLDERS");
 
   // If this property is defined, let the setter turn this on or off...
   //
   if (prop) {
-    return cmIsOn(prop);
+    return cmIsOn(*prop);
   }
 
   // By default, this feature is OFF, since it is not supported in the
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h
index 7dc4822..70e43b5 100644
--- a/Source/cmGlobalGenerator.h
+++ b/Source/cmGlobalGenerator.h
@@ -588,7 +588,7 @@
 
   cmGeneratorTarget* FindGeneratorTargetImpl(std::string const& name) const;
 
-  const char* GetPredefinedTargetsFolder();
+  std::string GetPredefinedTargetsFolder();
 
 private:
   using TargetMap = std::unordered_map<std::string, cmTarget*>;
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index 94483ca..062f8e9 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -1011,7 +1011,7 @@
       cm::make_unique<cmGeneratedFileStream>(buildFilePath);
     *this->CompileCommandsStream << "[\n";
   } else {
-    *this->CompileCommandsStream << "," << std::endl;
+    *this->CompileCommandsStream << ",\n";
   }
 
   std::string sourceFileName = sourceFile;
diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx
index e213023..582877f 100644
--- a/Source/cmGlobalUnixMakefileGenerator3.cxx
+++ b/Source/cmGlobalUnixMakefileGenerator3.cxx
@@ -159,7 +159,7 @@
   this->WriteMainCMakefile();
 
   if (this->CommandDatabase) {
-    *this->CommandDatabase << std::endl << "]";
+    *this->CommandDatabase << "\n]";
     this->CommandDatabase.reset();
   }
 }
@@ -174,21 +174,20 @@
       "/compile_commands.json";
     this->CommandDatabase =
       cm::make_unique<cmGeneratedFileStream>(commandDatabaseName);
-    *this->CommandDatabase << "[" << std::endl;
+    *this->CommandDatabase << "[\n";
   } else {
-    *this->CommandDatabase << "," << std::endl;
+    *this->CommandDatabase << ",\n";
   }
-  *this->CommandDatabase << "{" << std::endl
+  *this->CommandDatabase << "{\n"
                          << R"(  "directory": ")"
                          << cmGlobalGenerator::EscapeJSON(workingDirectory)
-                         << "\"," << std::endl
+                         << "\",\n"
                          << R"(  "command": ")"
                          << cmGlobalGenerator::EscapeJSON(compileCommand)
-                         << "\"," << std::endl
+                         << "\",\n"
                          << R"(  "file": ")"
-                         << cmGlobalGenerator::EscapeJSON(sourceFile) << "\""
-                         << std::endl
-                         << "}";
+                         << cmGlobalGenerator::EscapeJSON(sourceFile)
+                         << "\"\n}";
 }
 
 void cmGlobalUnixMakefileGenerator3::WriteMainMakefile2()
@@ -674,10 +673,10 @@
       }
 
       bool targetMessages = true;
-      if (const char* tgtMsg =
+      if (cmProp tgtMsg =
             this->GetCMakeInstance()->GetState()->GetGlobalProperty(
               "TARGET_MESSAGES")) {
-        targetMessages = cmIsOn(tgtMsg);
+        targetMessages = cmIsOn(*tgtMsg);
       }
 
       if (targetMessages) {
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index fab5bce..1c1aaad 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -3696,15 +3696,14 @@
 
 bool cmGlobalXCodeGenerator::UseEffectivePlatformName(cmMakefile* mf) const
 {
-  const char* epnValue =
-    this->GetCMakeInstance()->GetState()->GetGlobalProperty(
-      "XCODE_EMIT_EFFECTIVE_PLATFORM_NAME");
+  cmProp epnValue = this->GetCMakeInstance()->GetState()->GetGlobalProperty(
+    "XCODE_EMIT_EFFECTIVE_PLATFORM_NAME");
 
   if (!epnValue) {
     return mf->PlatformIsAppleEmbedded();
   }
 
-  return cmIsOn(epnValue);
+  return cmIsOn(*epnValue);
 }
 
 bool cmGlobalXCodeGenerator::ShouldStripResourcePath(cmMakefile*) const
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index e908180..0a78af6 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -2432,11 +2432,9 @@
   }
 
   for (std::string const& config : configsList) {
-    const std::string buildType = cmSystemTools::UpperCase(config);
-
     // FIXME: Refactor collection of sources to not evaluate object libraries.
     std::vector<cmSourceFile*> sources;
-    target->GetSourceFiles(sources, buildType);
+    target->GetSourceFiles(sources, config);
 
     for (const std::string& lang : { "C", "CXX", "OBJC", "OBJCXX" }) {
       auto langSources = std::count_if(
@@ -2605,15 +2603,13 @@
     config = this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
   }
 
-  const std::string buildType = cmSystemTools::UpperCase(config);
-
   std::string filename_base =
     cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles/",
              target->GetName(), ".dir/Unity/");
 
   // FIXME: Refactor collection of sources to not evaluate object libraries.
   std::vector<cmSourceFile*> sources;
-  target->GetSourceFiles(sources, buildType);
+  target->GetSourceFiles(sources, config);
 
   auto batchSizeString = target->GetProperty("UNITY_BUILD_BATCH_SIZE");
   const size_t unityBatchSize =
@@ -2965,8 +2961,8 @@
   }
   cmStateSnapshot snp = this->StateSnapshot;
   while (snp.IsValid()) {
-    if (const char* value = snp.GetDirectory().GetProperty(featureName)) {
-      return value;
+    if (cmProp value = snp.GetDirectory().GetProperty(featureName)) {
+      return value->c_str();
     }
     snp = snp.GetBuildsystemDirectoryParent();
   }
diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx
index 1568397..e62371d 100644
--- a/Source/cmLocalNinjaGenerator.cxx
+++ b/Source/cmLocalNinjaGenerator.cxx
@@ -207,9 +207,8 @@
 void cmLocalNinjaGenerator::WriteProjectHeader(std::ostream& os)
 {
   cmGlobalNinjaGenerator::WriteDivider(os);
-  os << "# Project: " << this->GetProjectName() << std::endl
-     << "# Configurations: " << cmJoin(this->GetConfigNames(), ", ")
-     << std::endl;
+  os << "# Project: " << this->GetProjectName() << '\n'
+     << "# Configurations: " << cmJoin(this->GetConfigNames(), ", ") << '\n';
   cmGlobalNinjaGenerator::WriteDivider(os);
 }
 
@@ -236,8 +235,7 @@
 
   cmGlobalNinjaGenerator::WriteComment(
     os, "Minimal version of Ninja required by this file");
-  os << "ninja_required_version = " << requiredVersion << std::endl
-     << std::endl;
+  os << "ninja_required_version = " << requiredVersion << "\n\n";
 }
 
 void cmLocalNinjaGenerator::WriteNinjaConfigurationVariable(
@@ -252,23 +250,22 @@
 {
   cmGlobalNinjaGenerator::WriteDivider(os);
 
-  const char* jobpools =
+  cmProp jobpools =
     this->GetCMakeInstance()->GetState()->GetGlobalProperty("JOB_POOLS");
   if (!jobpools) {
-    jobpools = this->GetMakefile()->GetDefinition("CMAKE_JOB_POOLS");
+    jobpools = this->GetMakefile()->GetDef("CMAKE_JOB_POOLS");
   }
   if (jobpools) {
     cmGlobalNinjaGenerator::WriteComment(
       os, "Pools defined by global property JOB_POOLS");
-    std::vector<std::string> pools = cmExpandedList(jobpools);
+    std::vector<std::string> pools = cmExpandedList(*jobpools);
     for (std::string const& pool : pools) {
       const std::string::size_type eq = pool.find('=');
       unsigned int jobs;
       if (eq != std::string::npos &&
           sscanf(pool.c_str() + eq, "=%u", &jobs) == 1) {
-        os << "pool " << pool.substr(0, eq) << std::endl;
-        os << "  depth = " << jobs << std::endl;
-        os << std::endl;
+        os << "pool " << pool.substr(0, eq) << "\n  depth = " << jobs
+           << "\n\n";
       } else {
         cmSystemTools::Error("Invalid pool defined by property 'JOB_POOLS': " +
                              pool);
@@ -280,8 +277,7 @@
 void cmLocalNinjaGenerator::WriteNinjaFilesInclusionConfig(std::ostream& os)
 {
   cmGlobalNinjaGenerator::WriteDivider(os);
-  os << "# Include auxiliary files.\n"
-     << "\n";
+  os << "# Include auxiliary files.\n\n";
   cmGlobalNinjaGenerator* ng = this->GetGlobalNinjaGenerator();
   std::string const ninjaCommonFile =
     ng->NinjaOutputPath(cmGlobalNinjaMultiGenerator::NINJA_COMMON_FILE);
@@ -294,8 +290,7 @@
 void cmLocalNinjaGenerator::WriteNinjaFilesInclusionCommon(std::ostream& os)
 {
   cmGlobalNinjaGenerator::WriteDivider(os);
-  os << "# Include auxiliary files.\n"
-     << "\n";
+  os << "# Include auxiliary files.\n\n";
   cmGlobalNinjaGenerator* ng = this->GetGlobalNinjaGenerator();
   std::string const ninjaRulesFile =
     ng->NinjaOutputPath(cmGlobalNinjaGenerator::NINJA_RULES_FILE);
@@ -308,14 +303,14 @@
 void cmLocalNinjaGenerator::WriteProcessedMakefile(std::ostream& os)
 {
   cmGlobalNinjaGenerator::WriteDivider(os);
-  os << "# Write statements declared in CMakeLists.txt:" << std::endl
+  os << "# Write statements declared in CMakeLists.txt:\n"
      << "# " << this->Makefile->GetDefinition("CMAKE_CURRENT_LIST_FILE")
-     << std::endl;
+     << '\n';
   if (this->IsRootMakefile()) {
-    os << "# Which is the root file." << std::endl;
+    os << "# Which is the root file.\n";
   }
   cmGlobalNinjaGenerator::WriteDivider(os);
-  os << std::endl;
+  os << '\n';
 }
 
 void cmLocalNinjaGenerator::AppendTargetOutputs(cmGeneratorTarget* target,
diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx
index 95c326a..593a6d9 100644
--- a/Source/cmLocalUnixMakefileGenerator3.cxx
+++ b/Source/cmLocalUnixMakefileGenerator3.cxx
@@ -725,6 +725,10 @@
       ;
     /* clang-format on */
   } else {
+    makefileStream << "# Command-line flag to silence nested $(MAKE).\n"
+                      "$(VERBOSE)MAKESILENT = -s\n"
+                      "\n";
+
     // Write special target to silence make output.  This must be after
     // the default target in case VERBOSE is set (which changes the
     // name).  The setting of CMAKE_VERBOSE_MAKEFILE to ON will cause a
@@ -1322,10 +1326,9 @@
     int result;
     if (!ftc->Compare(internalDependFile, tgtInfo, &result) || result < 0) {
       if (verbose) {
-        std::ostringstream msg;
-        msg << "Dependee \"" << tgtInfo << "\" is newer than depender \""
-            << internalDependFile << "\"." << std::endl;
-        cmSystemTools::Stdout(msg.str());
+        cmSystemTools::Stdout(cmStrCat("Dependee \"", tgtInfo,
+                                       "\" is newer than depender \"",
+                                       internalDependFile, "\".\n"));
       }
       needRescanDependInfo = true;
     }
@@ -1342,10 +1345,9 @@
     if (!ftc->Compare(internalDependFile, dirInfoFile, &result) ||
         result < 0) {
       if (verbose) {
-        std::ostringstream msg;
-        msg << "Dependee \"" << dirInfoFile << "\" is newer than depender \""
-            << internalDependFile << "\"." << std::endl;
-        cmSystemTools::Stdout(msg.str());
+        cmSystemTools::Stdout(cmStrCat("Dependee \"", dirInfoFile,
+                                       "\" is newer than depender \"",
+                                       internalDependFile, "\".\n"));
       }
       needRescanDirInfo = true;
     }
@@ -1511,11 +1513,9 @@
     if (cmSystemTools::FileExists(dependee) &&
         !cmSystemTools::FileExists(depender)) {
       if (verbose) {
-        std::ostringstream msg;
-        msg << "Deleting primary custom command output \"" << dependee
-            << "\" because another output \"" << depender
-            << "\" does not exist." << std::endl;
-        cmSystemTools::Stdout(msg.str());
+        cmSystemTools::Stdout(cmStrCat(
+          "Deleting primary custom command output \"", dependee,
+          "\" because another output \"", depender, "\" does not exist.\n"));
       }
       cmSystemTools::RemoveFile(dependee);
     }
@@ -1919,7 +1919,7 @@
 {
   // Call make on the given file.
   std::string cmd = cmStrCat(
-    "$(MAKE) -f ",
+    "$(MAKE) $(MAKESILENT) -f ",
     this->ConvertToOutputFormat(makefile, cmOutputConverter::SHELL), ' ');
 
   cmGlobalUnixMakefileGenerator3* gg =
@@ -1962,7 +1962,7 @@
 void cmLocalUnixMakefileGenerator3::WriteDivider(std::ostream& os)
 {
   os << "#======================================"
-     << "=======================================\n";
+        "=======================================\n";
 }
 
 void cmLocalUnixMakefileGenerator3::WriteCMakeArgument(std::ostream& os,
@@ -1970,7 +1970,7 @@
 {
   // Write the given string to the stream with escaping to get it back
   // into CMake through the lexical scanner.
-  os << "\"";
+  os << '"';
   for (char c : s) {
     if (c == '\\') {
       os << "\\\\";
@@ -1980,7 +1980,7 @@
       os << c;
     }
   }
-  os << "\"";
+  os << '"';
 }
 
 std::string cmLocalUnixMakefileGenerator3::ConvertToQuotedOutputPath(
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 2487f4d..812f922 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -4102,12 +4102,14 @@
     return output.c_str();
   }
 
-  return this->StateSnapshot.GetDirectory().GetProperty(prop);
+  cmProp retVal = this->StateSnapshot.GetDirectory().GetProperty(prop);
+  return retVal ? retVal->c_str() : nullptr;
 }
 
 const char* cmMakefile::GetProperty(const std::string& prop, bool chain) const
 {
-  return this->StateSnapshot.GetDirectory().GetProperty(prop, chain);
+  cmProp retVal = this->StateSnapshot.GetDirectory().GetProperty(prop, chain);
+  return retVal ? retVal->c_str() : nullptr;
 }
 
 bool cmMakefile::GetPropertyAsBool(const std::string& prop) const
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index f0fc34f..4fb84ee 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -46,9 +46,8 @@
     this->LocalGenerator->GetGlobalGenerator());
   cmake* cm = this->GlobalGenerator->GetCMakeInstance();
   this->NoRuleMessages = false;
-  if (const char* ruleStatus =
-        cm->GetState()->GetGlobalProperty("RULE_MESSAGES")) {
-    this->NoRuleMessages = cmIsOff(ruleStatus);
+  if (cmProp ruleStatus = cm->GetState()->GetGlobalProperty("RULE_MESSAGES")) {
+    this->NoRuleMessages = cmIsOff(*ruleStatus);
   }
   MacOSXContentGenerator = cm::make_unique<MacOSXContentGeneratorType>(this);
 }
@@ -300,8 +299,7 @@
       dependFileNameFull, false, this->GlobalGenerator->GetMakefileEncoding());
     depFileStream << "# Empty dependencies file for "
                   << this->GeneratorTarget->GetName() << ".\n"
-                  << "# This may be replaced when dependencies are built."
-                  << std::endl;
+                  << "# This may be replaced when dependencies are built.\n";
   }
 
   // Open the flags file.  This should be copy-if-different because the
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 @@
          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/Source/cmQtAutoGenGlobalInitializer.cxx b/Source/cmQtAutoGenGlobalInitializer.cxx
index 7a6cb42..18b135d 100644
--- a/Source/cmQtAutoGenGlobalInitializer.cxx
+++ b/Source/cmQtAutoGenGlobalInitializer.cxx
@@ -164,10 +164,10 @@
 
     // Set FOLDER property in the target
     {
-      char const* folder =
+      cmProp folder =
         makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER");
       if (folder != nullptr) {
-        target->SetProperty("FOLDER", folder);
+        target->SetProperty("FOLDER", *folder);
       }
     }
   }
diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx
index 629367d..dddba0a 100644
--- a/Source/cmQtAutoGenInitializer.cxx
+++ b/Source/cmQtAutoGenInitializer.cxx
@@ -339,15 +339,18 @@
 
   // Targets FOLDER
   {
-    const char* folder =
+    cmProp prop =
       this->Makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER");
-    if (folder == nullptr) {
-      folder = this->Makefile->GetState()->GetGlobalProperty(
+    if (prop == nullptr) {
+      prop = this->Makefile->GetState()->GetGlobalProperty(
         "AUTOGEN_TARGETS_FOLDER");
     }
+    const char* folder;
     // Inherit FOLDER property from target (#13688)
-    if (folder == nullptr) {
+    if (prop == nullptr) {
       folder = this->GenTarget->GetProperty("FOLDER");
+    } else {
+      folder = prop->c_str();
     }
     if (folder != nullptr) {
       this->TargetsFolder = folder;
@@ -1604,10 +1607,9 @@
         cmStrCat(genNameUpper, "_SOURCE_GROUP"), "AUTOGEN_SOURCE_GROUP"
       };
       for (std::string const& prop : props) {
-        const char* propName =
-          this->Makefile->GetState()->GetGlobalProperty(prop);
-        if ((propName != nullptr) && (*propName != '\0')) {
-          groupName = propName;
+        cmProp propName = this->Makefile->GetState()->GetGlobalProperty(prop);
+        if (propName && !propName->empty()) {
+          groupName = *propName;
           property = prop;
           break;
         }
diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx
index 83a7f7b..c5f3463 100644
--- a/Source/cmServerProtocol.cxx
+++ b/Source/cmServerProtocol.cxx
@@ -450,7 +450,7 @@
     bool haveProperties = false;
     for (auto const& prop : state->GetCacheEntryPropertyList(key)) {
       haveProperties = true;
-      props[prop] = state->GetCacheEntryProperty(key, prop);
+      props[prop] = *state->GetCacheEntryProperty(key, prop);
     }
     if (haveProperties) {
       entry[kPROPERTIES_KEY] = props;
diff --git a/Source/cmState.cxx b/Source/cmState.cxx
index 6d95c7a..5a113dd 100644
--- a/Source/cmState.cxx
+++ b/Source/cmState.cxx
@@ -206,15 +206,14 @@
   return it.GetPropertyList();
 }
 
-const char* cmState::GetCacheEntryProperty(std::string const& key,
-                                           std::string const& propertyName)
+cmProp cmState::GetCacheEntryProperty(std::string const& key,
+                                      std::string const& propertyName)
 {
   cmCacheManager::CacheIterator it = this->CacheManager->GetCacheIterator(key);
   if (!it.PropertyExists(propertyName)) {
     return nullptr;
   }
-  cmProp retVal = it.GetProperty(propertyName);
-  return retVal ? retVal->c_str() : nullptr;
+  return it.GetProperty(propertyName);
 }
 
 bool cmState::GetCacheEntryPropertyAsBool(std::string const& key,
@@ -587,7 +586,7 @@
   this->GlobalProperties.AppendProperty(prop, value, asString);
 }
 
-const char* cmState::GetGlobalProperty(const std::string& prop)
+cmProp cmState::GetGlobalProperty(const std::string& prop)
 {
   if (prop == "CACHE_VARIABLES") {
     std::vector<std::string> cacheKeys = this->GetCacheEntryKeys();
@@ -611,41 +610,59 @@
   }
 #define STRING_LIST_ELEMENT(F) ";" #F
   if (prop == "CMAKE_C_KNOWN_FEATURES") {
-    return &FOR_EACH_C_FEATURE(STRING_LIST_ELEMENT)[1];
+    static const std::string s_out(
+      &FOR_EACH_C_FEATURE(STRING_LIST_ELEMENT)[1]);
+    return &s_out;
   }
   if (prop == "CMAKE_C90_KNOWN_FEATURES") {
-    return &FOR_EACH_C90_FEATURE(STRING_LIST_ELEMENT)[1];
+    static const std::string s_out(
+      &FOR_EACH_C90_FEATURE(STRING_LIST_ELEMENT)[1]);
+    return &s_out;
   }
   if (prop == "CMAKE_C99_KNOWN_FEATURES") {
-    return &FOR_EACH_C99_FEATURE(STRING_LIST_ELEMENT)[1];
+    static const std::string s_out(
+      &FOR_EACH_C99_FEATURE(STRING_LIST_ELEMENT)[1]);
+    return &s_out;
   }
   if (prop == "CMAKE_C11_KNOWN_FEATURES") {
-    return &FOR_EACH_C11_FEATURE(STRING_LIST_ELEMENT)[1];
+    static const std::string s_out(
+      &FOR_EACH_C11_FEATURE(STRING_LIST_ELEMENT)[1]);
+    return &s_out;
   }
   if (prop == "CMAKE_CXX_KNOWN_FEATURES") {
-    return &FOR_EACH_CXX_FEATURE(STRING_LIST_ELEMENT)[1];
+    static const std::string s_out(
+      &FOR_EACH_CXX_FEATURE(STRING_LIST_ELEMENT)[1]);
+    return &s_out;
   }
   if (prop == "CMAKE_CXX98_KNOWN_FEATURES") {
-    return &FOR_EACH_CXX98_FEATURE(STRING_LIST_ELEMENT)[1];
+    static const std::string s_out(
+      &FOR_EACH_CXX98_FEATURE(STRING_LIST_ELEMENT)[1]);
+    return &s_out;
   }
   if (prop == "CMAKE_CXX11_KNOWN_FEATURES") {
-    return &FOR_EACH_CXX11_FEATURE(STRING_LIST_ELEMENT)[1];
+    static const std::string s_out(
+      &FOR_EACH_CXX11_FEATURE(STRING_LIST_ELEMENT)[1]);
+    return &s_out;
   }
   if (prop == "CMAKE_CXX14_KNOWN_FEATURES") {
-    return &FOR_EACH_CXX14_FEATURE(STRING_LIST_ELEMENT)[1];
+    static const std::string s_out(
+      &FOR_EACH_CXX14_FEATURE(STRING_LIST_ELEMENT)[1]);
+    return &s_out;
   }
   if (prop == "CMAKE_CUDA_KNOWN_FEATURES") {
-    return &FOR_EACH_CUDA_FEATURE(STRING_LIST_ELEMENT)[1];
+    static const std::string s_out(
+      &FOR_EACH_CUDA_FEATURE(STRING_LIST_ELEMENT)[1]);
+    return &s_out;
   }
 
 #undef STRING_LIST_ELEMENT
-  cmProp retVal = this->GlobalProperties.GetPropertyValue(prop);
-  return retVal ? retVal->c_str() : nullptr;
+  return this->GlobalProperties.GetPropertyValue(prop);
 }
 
 bool cmState::GetGlobalPropertyAsBool(const std::string& prop)
 {
-  return cmIsOn(this->GetGlobalProperty(prop));
+  cmProp p = this->GetGlobalProperty(prop);
+  return p && cmIsOn(*p);
 }
 
 void cmState::SetSourceDirectory(std::string const& sourceDirectory)
diff --git a/Source/cmState.h b/Source/cmState.h
index 89400d6..e3fbfdc 100644
--- a/Source/cmState.h
+++ b/Source/cmState.h
@@ -105,8 +105,8 @@
   void SetCacheEntryBoolProperty(std::string const& key,
                                  std::string const& propertyName, bool value);
   std::vector<std::string> GetCacheEntryPropertyList(std::string const& key);
-  const char* GetCacheEntryProperty(std::string const& key,
-                                    std::string const& propertyName);
+  cmProp GetCacheEntryProperty(std::string const& key,
+                               std::string const& propertyName);
   bool GetCacheEntryPropertyAsBool(std::string const& key,
                                    std::string const& propertyName);
   void AppendCacheEntryProperty(std::string const& key,
@@ -174,7 +174,7 @@
   void SetGlobalProperty(const std::string& prop, const char* value);
   void AppendGlobalProperty(const std::string& prop, const std::string& value,
                             bool asString = false);
-  const char* GetGlobalProperty(const std::string& prop);
+  cmProp GetGlobalProperty(const std::string& prop);
   bool GetGlobalPropertyAsBool(const std::string& prop);
 
   std::string const& GetSourceDirectory() const;
diff --git a/Source/cmStateDirectory.cxx b/Source/cmStateDirectory.cxx
index e7de3c7..a4fe663 100644
--- a/Source/cmStateDirectory.cxx
+++ b/Source/cmStateDirectory.cxx
@@ -548,32 +548,31 @@
   this->DirectoryState->Properties.AppendProperty(prop, value, asString);
 }
 
-const char* cmStateDirectory::GetProperty(const std::string& prop) const
+cmProp cmStateDirectory::GetProperty(const std::string& prop) const
 {
   const bool chain =
     this->Snapshot_.State->IsPropertyChained(prop, cmProperty::DIRECTORY);
   return this->GetProperty(prop, chain);
 }
 
-const char* cmStateDirectory::GetProperty(const std::string& prop,
-                                          bool chain) const
+cmProp cmStateDirectory::GetProperty(const std::string& prop, bool chain) const
 {
   static std::string output;
   output.clear();
   if (prop == "PARENT_DIRECTORY") {
     cmStateSnapshot parent = this->Snapshot_.GetBuildsystemDirectoryParent();
     if (parent.IsValid()) {
-      return parent.GetDirectory().GetCurrentSource().c_str();
+      return &parent.GetDirectory().GetCurrentSource();
     }
-    return "";
+    return &output;
   }
   if (prop == kBINARY_DIR) {
     output = this->GetCurrentBinary();
-    return output.c_str();
+    return &output;
   }
   if (prop == kSOURCE_DIR) {
     output = this->GetCurrentSource();
-    return output.c_str();
+    return &output;
   }
   if (prop == kSUBDIRECTORIES) {
     std::vector<std::string> child_dirs;
@@ -584,11 +583,11 @@
       child_dirs.push_back(ci.GetDirectory().GetCurrentSource());
     }
     output = cmJoin(child_dirs, ";");
-    return output.c_str();
+    return &output;
   }
   if (prop == kBUILDSYSTEM_TARGETS) {
     output = cmJoin(this->DirectoryState->NormalTargetNames, ";");
-    return output.c_str();
+    return &output;
   }
 
   if (prop == "LISTFILE_STACK") {
@@ -600,38 +599,38 @@
     }
     std::reverse(listFiles.begin(), listFiles.end());
     output = cmJoin(listFiles, ";");
-    return output.c_str();
+    return &output;
   }
   if (prop == "CACHE_VARIABLES") {
     output = cmJoin(this->Snapshot_.State->GetCacheEntryKeys(), ";");
-    return output.c_str();
+    return &output;
   }
   if (prop == "VARIABLES") {
     std::vector<std::string> res = this->Snapshot_.ClosureKeys();
     cm::append(res, this->Snapshot_.State->GetCacheEntryKeys());
     std::sort(res.begin(), res.end());
     output = cmJoin(res, ";");
-    return output.c_str();
+    return &output;
   }
   if (prop == "INCLUDE_DIRECTORIES") {
     output = cmJoin(this->GetIncludeDirectoriesEntries(), ";");
-    return output.c_str();
+    return &output;
   }
   if (prop == "COMPILE_OPTIONS") {
     output = cmJoin(this->GetCompileOptionsEntries(), ";");
-    return output.c_str();
+    return &output;
   }
   if (prop == "COMPILE_DEFINITIONS") {
     output = cmJoin(this->GetCompileDefinitionsEntries(), ";");
-    return output.c_str();
+    return &output;
   }
   if (prop == "LINK_OPTIONS") {
     output = cmJoin(this->GetLinkOptionsEntries(), ";");
-    return output.c_str();
+    return &output;
   }
   if (prop == "LINK_DIRECTORIES") {
     output = cmJoin(this->GetLinkDirectoriesEntries(), ";");
-    return output.c_str();
+    return &output;
   }
 
   cmProp retVal = this->DirectoryState->Properties.GetPropertyValue(prop);
@@ -644,12 +643,13 @@
     return this->Snapshot_.State->GetGlobalProperty(prop);
   }
 
-  return retVal ? retVal->c_str() : nullptr;
+  return retVal;
 }
 
 bool cmStateDirectory::GetPropertyAsBool(const std::string& prop) const
 {
-  return cmIsOn(this->GetProperty(prop));
+  cmProp p = this->GetProperty(prop);
+  return p && cmIsOn(*p);
 }
 
 std::vector<std::string> cmStateDirectory::GetPropertyKeys() const
diff --git a/Source/cmStateDirectory.h b/Source/cmStateDirectory.h
index 53a2d54..8144160 100644
--- a/Source/cmStateDirectory.h
+++ b/Source/cmStateDirectory.h
@@ -16,6 +16,8 @@
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 
+using cmProp = const std::string*;
+
 class cmStateDirectory
 {
   cmStateDirectory(
@@ -86,8 +88,8 @@
                    cmListFileBacktrace const& lfbt);
   void AppendProperty(const std::string& prop, const std::string& value,
                       bool asString, cmListFileBacktrace const& lfbt);
-  const char* GetProperty(const std::string& prop) const;
-  const char* GetProperty(const std::string& prop, bool chain) const;
+  cmProp GetProperty(const std::string& prop) const;
+  cmProp GetProperty(const std::string& prop, bool chain) const;
   bool GetPropertyAsBool(const std::string& prop) const;
   std::vector<std::string> GetPropertyKeys() const;
 
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 3838356..93c168a 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -368,6 +368,7 @@
     initProp("DISABLE_PRECOMPILE_HEADERS");
     initProp("UNITY_BUILD");
     initPropValue("UNITY_BUILD_BATCH_SIZE", "8");
+    initPropValue("PCH_WARN_INVALID", "ON");
 #ifdef __APPLE__
     if (this->GetGlobalGenerator()->IsXcode()) {
       initProp("XCODE_SCHEME_ADDRESS_SANITIZER");
@@ -1778,8 +1779,11 @@
     const bool chain =
       impl->Makefile->GetState()->IsPropertyChained(prop, cmProperty::TARGET);
     if (chain) {
-      return impl->Makefile->GetStateSnapshot().GetDirectory().GetProperty(
+      retVal = impl->Makefile->GetStateSnapshot().GetDirectory().GetProperty(
         prop, chain);
+      if (retVal) {
+        return retVal->c_str();
+      }
     }
     return nullptr;
   }
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 1273308..abc8b6f 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -1745,20 +1745,65 @@
   }
 }
 
+void cmVisualStudio10TargetGenerator::ParseSettingsProperty(
+  const char* settingsPropertyValue, ConfigToSettings& toolSettings)
+{
+  if (settingsPropertyValue) {
+    cmGeneratorExpression ge;
+
+    std::unique_ptr<cmCompiledGeneratorExpression> cge =
+      ge.Parse(settingsPropertyValue);
+
+    for (const std::string& config : this->Configurations) {
+      std::string evaluated = cge->Evaluate(this->LocalGenerator, config);
+
+      std::vector<std::string> settings = cmExpandedList(evaluated);
+      for (const std::string& setting : settings) {
+        const std::string::size_type assignment = setting.find('=');
+        if (assignment != std::string::npos) {
+          const std::string propName = setting.substr(0, assignment);
+          const std::string propValue = setting.substr(assignment + 1);
+
+          if (!propValue.empty()) {
+            toolSettings[config][propName] = propValue;
+          }
+        }
+      }
+    }
+  }
+}
+
+bool cmVisualStudio10TargetGenerator::PropertyIsSameInAllConfigs(
+  const ConfigToSettings& toolSettings, const std::string& propName)
+{
+  std::string firstPropValue = "";
+  for (const auto& configToSettings : toolSettings) {
+    const std::unordered_map<std::string, std::string>& settings =
+      configToSettings.second;
+
+    if (firstPropValue.empty()) {
+      if (settings.find(propName) != settings.end()) {
+        firstPropValue = settings.find(propName)->second;
+      }
+    }
+
+    if (settings.find(propName) == settings.end()) {
+      return false;
+    }
+
+    if (settings.find(propName)->second != firstPropValue) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 void cmVisualStudio10TargetGenerator::WriteExtraSource(Elem& e1,
                                                        cmSourceFile const* sf)
 {
   bool toolHasSettings = false;
   const char* tool = "None";
-  std::string shaderType;
-  std::string shaderEntryPoint;
-  std::string shaderModel;
-  std::string shaderAdditionalFlags;
-  std::string shaderDisableOptimizations;
-  std::string shaderEnableDebug;
-  std::string shaderObjectFileName;
-  std::string outputHeaderFile;
-  std::string variableName;
   std::string settingsGenerator;
   std::string settingsLastGenOutput;
   std::string sourceLink;
@@ -1766,6 +1811,11 @@
   std::string copyToOutDir;
   std::string includeInVsix;
   std::string ext = cmSystemTools::LowerCase(sf->GetExtension());
+  ConfigToSettings toolSettings;
+  for (const auto& config : this->Configurations) {
+    toolSettings[config];
+  }
+
   if (this->ProjectType == csproj && !this->InSourceBuild) {
     toolHasSettings = true;
   }
@@ -1773,47 +1823,72 @@
     tool = "FXCompile";
     // Figure out the type of shader compiler to use.
     if (const char* st = sf->GetProperty("VS_SHADER_TYPE")) {
-      shaderType = st;
-      toolHasSettings = true;
+      for (const std::string& config : this->Configurations) {
+        toolSettings[config]["ShaderType"] = st;
+      }
     }
     // Figure out which entry point to use if any
     if (const char* se = sf->GetProperty("VS_SHADER_ENTRYPOINT")) {
-      shaderEntryPoint = se;
-      toolHasSettings = true;
+      for (const std::string& config : this->Configurations) {
+        toolSettings[config]["EntryPointName"] = se;
+      }
     }
     // Figure out which shader model to use if any
     if (const char* sm = sf->GetProperty("VS_SHADER_MODEL")) {
-      shaderModel = sm;
-      toolHasSettings = true;
+      for (const std::string& config : this->Configurations) {
+        toolSettings[config]["ShaderModel"] = sm;
+      }
     }
     // Figure out which output header file to use if any
     if (const char* ohf = sf->GetProperty("VS_SHADER_OUTPUT_HEADER_FILE")) {
-      outputHeaderFile = ohf;
-      toolHasSettings = true;
+      for (const std::string& config : this->Configurations) {
+        toolSettings[config]["HeaderFileOutput"] = ohf;
+      }
     }
     // Figure out which variable name to use if any
     if (const char* vn = sf->GetProperty("VS_SHADER_VARIABLE_NAME")) {
-      variableName = vn;
-      toolHasSettings = true;
+      for (const std::string& config : this->Configurations) {
+        toolSettings[config]["VariableName"] = vn;
+      }
     }
     // Figure out if there's any additional flags to use
     if (const char* saf = sf->GetProperty("VS_SHADER_FLAGS")) {
-      shaderAdditionalFlags = saf;
-      toolHasSettings = true;
+      for (const std::string& config : this->Configurations) {
+        toolSettings[config]["AdditionalOptions"] = saf;
+      }
     }
     // Figure out if debug information should be generated
     if (const char* sed = sf->GetProperty("VS_SHADER_ENABLE_DEBUG")) {
-      shaderEnableDebug = sed;
-      toolHasSettings = true;
+      cmGeneratorExpression ge;
+      std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(sed);
+
+      for (const std::string& config : this->Configurations) {
+        std::string evaluated = cge->Evaluate(this->LocalGenerator, config);
+
+        if (!evaluated.empty()) {
+          toolSettings[config]["EnableDebuggingInformation"] =
+            cmIsOn(evaluated) ? "true" : "false";
+        }
+      }
     }
     // Figure out if optimizations should be disabled
     if (const char* sdo = sf->GetProperty("VS_SHADER_DISABLE_OPTIMIZATIONS")) {
-      shaderDisableOptimizations = sdo;
-      toolHasSettings = true;
+      cmGeneratorExpression ge;
+      std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(sdo);
+
+      for (const std::string& config : this->Configurations) {
+        std::string evaluated = cge->Evaluate(this->LocalGenerator, config);
+
+        if (!evaluated.empty()) {
+          toolSettings[config]["DisableOptimizations"] =
+            cmIsOn(evaluated) ? "true" : "false";
+        }
+      }
     }
     if (const char* sofn = sf->GetProperty("VS_SHADER_OBJECT_FILE_NAME")) {
-      shaderObjectFileName = sofn;
-      toolHasSettings = true;
+      for (const std::string& config : this->Configurations) {
+        toolSettings[config]["ObjectFileOutput"] = sofn;
+      }
     }
   } else if (ext == "jpg" || ext == "png") {
     tool = "Image";
@@ -1883,11 +1958,55 @@
     }
   }
 
+  if (ParsedToolTargetSettings.find(tool) == ParsedToolTargetSettings.end()) {
+    const char* toolTargetProperty =
+      this->GeneratorTarget->Target->GetProperty("VS_SOURCE_SETTINGS_" +
+                                                 std::string(tool));
+    ConfigToSettings toolTargetSettings;
+    ParseSettingsProperty(toolTargetProperty, toolTargetSettings);
+
+    ParsedToolTargetSettings[tool] = toolTargetSettings;
+  }
+
+  for (const auto& configToSetting : ParsedToolTargetSettings[tool]) {
+    for (const auto& setting : configToSetting.second) {
+      toolSettings[configToSetting.first][setting.first] = setting.second;
+    }
+  }
+
+  ParseSettingsProperty(sf->GetProperty("VS_SETTINGS"), toolSettings);
+
+  if (!toolSettings.empty()) {
+    toolHasSettings = true;
+  }
+
   Elem e2(e1, tool);
   this->WriteSource(e2, sf);
   if (toolHasSettings) {
     e2.SetHasElements();
 
+    std::vector<std::string> writtenSettings;
+    for (const auto& configSettings : toolSettings) {
+      for (const auto& setting : configSettings.second) {
+
+        if (std::find(writtenSettings.begin(), writtenSettings.end(),
+                      setting.first) != writtenSettings.end()) {
+          continue;
+        }
+
+        if (PropertyIsSameInAllConfigs(toolSettings, setting.first)) {
+          e2.Element(setting.first, setting.second);
+          writtenSettings.push_back(setting.first);
+        } else {
+          e2.WritePlatformConfigTag(setting.first,
+                                    "'$(Configuration)|$(Platform)'=='" +
+                                      configSettings.first + "|" +
+                                      this->Platform + "'",
+                                    setting.second);
+        }
+      }
+    }
+
     if (!deployContent.empty()) {
       cmGeneratorExpression ge;
       std::unique_ptr<cmCompiledGeneratorExpression> cge =
@@ -1913,73 +2032,7 @@
         }
       }
     }
-    if (!shaderType.empty()) {
-      e2.Element("ShaderType", shaderType);
-    }
-    if (!shaderEntryPoint.empty()) {
-      e2.Element("EntryPointName", shaderEntryPoint);
-    }
-    if (!shaderModel.empty()) {
-      e2.Element("ShaderModel", shaderModel);
-    }
-    if (!outputHeaderFile.empty()) {
-      for (size_t i = 0; i != this->Configurations.size(); ++i) {
-        e2.WritePlatformConfigTag("HeaderFileOutput",
-                                  "'$(Configuration)|$(Platform)'=='" +
-                                    this->Configurations[i] + "|" +
-                                    this->Platform + "'",
-                                  outputHeaderFile);
-      }
-    }
-    if (!variableName.empty()) {
-      for (size_t i = 0; i != this->Configurations.size(); ++i) {
-        e2.WritePlatformConfigTag("VariableName",
-                                  "'$(Configuration)|$(Platform)'=='" +
-                                    this->Configurations[i] + "|" +
-                                    this->Platform + "'",
-                                  variableName);
-      }
-    }
-    if (!shaderEnableDebug.empty()) {
-      cmGeneratorExpression ge;
-      std::unique_ptr<cmCompiledGeneratorExpression> cge =
-        ge.Parse(shaderEnableDebug);
 
-      for (size_t i = 0; i != this->Configurations.size(); ++i) {
-        const std::string& enableDebug =
-          cge->Evaluate(this->LocalGenerator, this->Configurations[i]);
-        if (!enableDebug.empty()) {
-          e2.WritePlatformConfigTag("EnableDebuggingInformation",
-                                    "'$(Configuration)|$(Platform)'=='" +
-                                      this->Configurations[i] + "|" +
-                                      this->Platform + "'",
-                                    cmIsOn(enableDebug) ? "true" : "false");
-        }
-      }
-    }
-    if (!shaderDisableOptimizations.empty()) {
-      cmGeneratorExpression ge;
-      std::unique_ptr<cmCompiledGeneratorExpression> cge =
-        ge.Parse(shaderDisableOptimizations);
-
-      for (size_t i = 0; i != this->Configurations.size(); ++i) {
-        const std::string& disableOptimizations =
-          cge->Evaluate(this->LocalGenerator, this->Configurations[i]);
-        if (!disableOptimizations.empty()) {
-          e2.WritePlatformConfigTag(
-            "DisableOptimizations",
-            "'$(Configuration)|$(Platform)'=='" + this->Configurations[i] +
-              "|" + this->Platform + "'",
-            (cmIsOn(disableOptimizations) ? "true" : "false"));
-        }
-      }
-    }
-    if (!shaderObjectFileName.empty()) {
-      e2.Element("ObjectFileOutput", shaderObjectFileName);
-    }
-    if (!shaderAdditionalFlags.empty()) {
-      e2.Element("AdditionalOptions", shaderAdditionalFlags);
-    }
     if (!settingsGenerator.empty()) {
       e2.Element("Generator", settingsGenerator);
     }
diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h
index 25b3d02..e588de8 100644
--- a/Source/cmVisualStudio10TargetGenerator.h
+++ b/Source/cmVisualStudio10TargetGenerator.h
@@ -10,6 +10,7 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 class cmComputeLinkInformation;
@@ -234,6 +235,15 @@
 
   using ToolSourceMap = std::map<std::string, ToolSources>;
   ToolSourceMap Tools;
+
+  using ConfigToSettings =
+    std::unordered_map<std::string,
+                       std::unordered_map<std::string, std::string>>;
+  std::unordered_map<std::string, ConfigToSettings> ParsedToolTargetSettings;
+  bool PropertyIsSameInAllConfigs(const ConfigToSettings& toolSettings,
+                                  const std::string& propName);
+  void ParseSettingsProperty(const char* settingsPropertyValue,
+                             ConfigToSettings& toolSettings);
   std::string GetCMakeFilePath(const char* name) const;
 };
 
diff --git a/Source/cmWriteFileCommand.cxx b/Source/cmWriteFileCommand.cxx
index 34e21b2..666ba87 100644
--- a/Source/cmWriteFileCommand.cxx
+++ b/Source/cmWriteFileCommand.cxx
@@ -72,7 +72,7 @@
     status.SetError(error);
     return false;
   }
-  file << message << std::endl;
+  file << message << '\n';
   file.close();
   if (mode && !writable) {
     cmSystemTools::SetPermissions(fileName.c_str(), mode);
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 59e7a37..2ec893f 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -1410,9 +1410,9 @@
     cmProp existingValue = this->State->GetCacheEntryValue(save.key);
     if (existingValue) {
       save.type = this->State->GetCacheEntryType(save.key);
-      if (const char* help =
+      if (cmProp help =
             this->State->GetCacheEntryProperty(save.key, "HELPSTRING")) {
-        save.help = help;
+        save.help = *help;
       }
     }
     saved.push_back(std::move(save));
@@ -1511,10 +1511,10 @@
   this->Messenger->SetDevWarningsAsErrors(value && cmIsOff(*value));
 
   int ret = this->ActualConfigure();
-  const char* delCacheVars =
+  cmProp delCacheVars =
     this->State->GetGlobalProperty("__CMAKE_DELETE_CACHE_CHANGE_VARS_");
-  if (delCacheVars && delCacheVars[0] != 0) {
-    return this->HandleDeleteCacheVariables(delCacheVars);
+  if (delCacheVars && !delCacheVars->empty()) {
+    return this->HandleDeleteCacheVariables(*delCacheVars);
   }
   return ret;
 }
@@ -2430,7 +2430,7 @@
   this->State->AppendGlobalProperty(prop, value, asString);
 }
 
-const char* cmake::GetProperty(const std::string& prop)
+cmProp cmake::GetProperty(const std::string& prop)
 {
   return this->State->GetGlobalProperty(prop);
 }
@@ -2671,10 +2671,10 @@
 std::vector<std::string> cmake::GetDebugConfigs()
 {
   std::vector<std::string> configs;
-  if (const char* config_list =
+  if (cmProp config_list =
         this->State->GetGlobalProperty("DEBUG_CONFIGURATIONS")) {
     // Expand the specified list and convert to upper-case.
-    cmExpandList(config_list, configs);
+    cmExpandList(*config_list, configs);
     std::transform(configs.begin(), configs.end(), configs.begin(),
                    cmSystemTools::UpperCase);
   }
diff --git a/Source/cmake.h b/Source/cmake.h
index 0f6440f..cfcd264 100644
--- a/Source/cmake.h
+++ b/Source/cmake.h
@@ -366,7 +366,7 @@
   void SetProperty(const std::string& prop, const char* value);
   void AppendProperty(const std::string& prop, const std::string& value,
                       bool asString = false);
-  const char* GetProperty(const std::string& prop);
+  cmProp GetProperty(const std::string& prop);
   bool GetPropertyAsBool(const std::string& prop);
 
   //! Get or create an cmInstalledFile instance and return a pointer to it
diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx
index 0e99117..84d0538 100644
--- a/Source/cmakemain.cxx
+++ b/Source/cmakemain.cxx
@@ -292,13 +292,13 @@
       cmStateEnums::CacheEntryType t = cm.GetState()->GetCacheEntryType(k);
       if (t != cmStateEnums::INTERNAL && t != cmStateEnums::STATIC &&
           t != cmStateEnums::UNINITIALIZED) {
-        const char* advancedProp =
+        cmProp advancedProp =
           cm.GetState()->GetCacheEntryProperty(k, "ADVANCED");
         if (list_all_cached || !advancedProp) {
           if (list_help) {
-            std::cout << "// "
-                      << cm.GetState()->GetCacheEntryProperty(k, "HELPSTRING")
-                      << std::endl;
+            cmProp help =
+              cm.GetState()->GetCacheEntryProperty(k, "HELPSTRING");
+            std::cout << "// " << (help ? *help : "") << std::endl;
           }
           std::cout << k << ":" << cmState::CacheEntryTypeToString(t) << "="
                     << cm.GetState()->GetSafeCacheEntryValue(k) << std::endl;
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index a395089..18a2108 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -1698,6 +1698,11 @@
   }
   auto status = process.GetStatus();
   if (!status[0] || status[0]->ExitStatus != 0) {
+    auto errorStream = process.ErrorStream();
+    if (errorStream) {
+      std::cerr << errorStream->rdbuf();
+    }
+
     return 1;
   }
 
@@ -1766,6 +1771,10 @@
   }
   auto status = process.GetStatus();
   if (!status[0] || status[0]->ExitStatus != 0) {
+    auto errorStream = process.ErrorStream();
+    if (errorStream) {
+      std::cerr << errorStream->rdbuf();
+    }
     return 1;
   }
 
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 101e10c..2a4af3e 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -665,7 +665,7 @@
 
 add_RunCMake_test("CTestCommandExpandLists")
 
-add_RunCMake_test(PrecompileHeaders)
+add_RunCMake_test(PrecompileHeaders -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID})
 add_RunCMake_test("UnityBuild")
 
 add_RunCMake_test(cmake_command)
diff --git a/Tests/RunCMake/CTestTimeout/Basic-stdout.txt b/Tests/RunCMake/CTestTimeout/Basic-stdout.txt
index 30ed178..db59dbf 100644
--- a/Tests/RunCMake/CTestTimeout/Basic-stdout.txt
+++ b/Tests/RunCMake/CTestTimeout/Basic-stdout.txt
@@ -1,6 +1,6 @@
 Test project [^
 ]*/Tests/RunCMake/CTestTimeout/Basic-build
     Start 1: TestTimeout
-1/1 Test #1: TestTimeout ......................\*\*\*Timeout +[0-9.]+ sec
+1/1 Test #1: TestTimeout ......................\*\*\*Timeout +[1-9][0-9.]* sec
 +
 0% tests passed, 1 tests failed out of 1
diff --git a/Tests/RunCMake/CTestTimeout/Fork-stdout.txt b/Tests/RunCMake/CTestTimeout/Fork-stdout.txt
index 284e4b1..2938d8e 100644
--- a/Tests/RunCMake/CTestTimeout/Fork-stdout.txt
+++ b/Tests/RunCMake/CTestTimeout/Fork-stdout.txt
@@ -1,6 +1,6 @@
 Test project [^
 ]*/Tests/RunCMake/CTestTimeout/Fork-build
     Start 1: TestTimeout
-1/1 Test #1: TestTimeout ......................\*\*\*Timeout +[0-9.]+ sec
+1/1 Test #1: TestTimeout ......................\*\*\*Timeout +[1-9][0-9.]* sec
 +
 0% tests passed, 1 tests failed out of 1
diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
index 41f13db..f95a6ee 100644
--- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
@@ -673,12 +673,11 @@
   set(RunCMake_TEST_NO_CLEAN 1)
   file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
   file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
-
   run_cmake_command(llvm_rc_no_args ${CMAKE_COMMAND} -E cmake_llvm_rc)
   run_cmake_command(llvm_rc_no_-- ${CMAKE_COMMAND} -E cmake_llvm_rc test.tmp ${CMAKE_COMMAND} -E echo "This is a test")
   run_cmake_command(llvm_rc_empty_preprocessor ${CMAKE_COMMAND} -E cmake_llvm_rc test.tmp -- ${CMAKE_COMMAND} -E echo "This is a test")
-  run_cmake_command(llvm_rc_failing_first_command ${CMAKE_COMMAND} -E cmake_llvm_rc test.tmp ${CMAKE_COMMAND} -E false -- ${CMAKE_COMMAND} -E echo "This is a test")
-  run_cmake_command(llvm_rc_failing_second_command ${CMAKE_COMMAND} -E cmake_llvm_rc test.tmp ${CMAKE_COMMAND} -E echo "This is a test" -- ${CMAKE_COMMAND} -E false )
+  run_cmake_command(llvm_rc_failing_first_command ${CMAKE_COMMAND} -E cmake_llvm_rc test.tmp ${CMAKE_COMMAND} -P FailedProgram.cmake -- ${CMAKE_COMMAND} -E echo "This is a test")
+  run_cmake_command(llvm_rc_failing_second_command ${CMAKE_COMMAND} -E cmake_llvm_rc test.tmp ${CMAKE_COMMAND} -E echo "This is a test" -- ${CMAKE_COMMAND} -P FailedProgram.cmake )
   if(EXISTS ${RunCMake_TEST_BINARY_DIR}/test.tmp)
       message(SEND_ERROR "${test} - FAILED:\n"
         "test.tmp was not deleted")
diff --git a/Tests/RunCMake/CommandLine/llvm_rc_failing_first_command-stderr.txt b/Tests/RunCMake/CommandLine/llvm_rc_failing_first_command-stderr.txt
new file mode 100644
index 0000000..765b708
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/llvm_rc_failing_first_command-stderr.txt
@@ -0,0 +1 @@
+CMake Error
diff --git a/Tests/RunCMake/CommandLine/llvm_rc_failing_second_command-stderr.txt b/Tests/RunCMake/CommandLine/llvm_rc_failing_second_command-stderr.txt
new file mode 100644
index 0000000..765b708
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/llvm_rc_failing_second_command-stderr.txt
@@ -0,0 +1 @@
+CMake Error
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-configuration-debug-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-configuration-debug-stdout.txt
new file mode 100644
index 0000000..1f5d1a5
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTest-configuration-debug-stdout.txt
@@ -0,0 +1,5 @@
+Test project .*GoogleTest-discovery-multi-config
+[ \t]*Test #[0-9]+: configuration\.case_release \(Disabled\)
+[ \t]*Test #[0-9]+: configuration\.case_debug
++
+Total Tests: 2
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-configuration-release-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-configuration-release-stdout.txt
new file mode 100644
index 0000000..4f91664
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTest-configuration-release-stdout.txt
@@ -0,0 +1,5 @@
+Test project .*GoogleTest-discovery-multi-config
+[ \t]*Test #[0-9]+: configuration\.case_release
+[ \t]*Test #[0-9]+: configuration\.case_debug \(Disabled\)
++
+Total Tests: 2
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-build-result.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-build-result.txt
similarity index 100%
rename from Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-build-result.txt
rename to Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-build-result.txt
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-build-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-build-stdout.txt
similarity index 100%
rename from Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-build-stdout.txt
rename to Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-build-stdout.txt
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-test-result.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-test-result.txt
similarity index 100%
rename from Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-test-result.txt
rename to Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-test-result.txt
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-test-stderr.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-test-stderr.txt
similarity index 100%
rename from Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-test-stderr.txt
rename to Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-test-stderr.txt
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-test-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-test-stdout.txt
similarity index 93%
rename from Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-test-stdout.txt
rename to Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-test-stdout.txt
index d4c4e7b..d9de3f8 100644
--- a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-test-stdout.txt
+++ b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-test-stdout.txt
@@ -1,4 +1,4 @@
-Test project .*GoogleTest-build
+Test project .*GoogleTest-discovery-timeout
 [ \t]*Start [0-9]+: discovery_timeout_test_NOT_BUILT
 Could not find executable discovery_timeout_test_NOT_BUILT
 Looked in the following places:
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stderr.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stderr.txt
new file mode 100644
index 0000000..75afe4a
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at .*GoogleTestAddTests.cmake:[0-9]+ \(message\):
+[ \t]*Error running test executable.
++
+[ \t]*Path: '.*discovery_timeout_test(\.exe)?'
+[ \t]*Result: Process terminated due to timeout
+[ \t]*Output:
+[ \t]*timeout.
+[ \t]*case
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stdout.txt
new file mode 100644
index 0000000..d65061f
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stdout.txt
@@ -0,0 +1 @@
+Test project .*GoogleTest-discovery-timeout
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest.cmake b/Tests/RunCMake/GoogleTest/GoogleTest.cmake
index 31808c6..4bc6b9d 100644
--- a/Tests/RunCMake/GoogleTest/GoogleTest.cmake
+++ b/Tests/RunCMake/GoogleTest/GoogleTest.cmake
@@ -49,11 +49,3 @@
   DISCOVERY_TIMEOUT 20
   PROPERTIES TIMEOUT 2
 )
-
-add_executable(discovery_timeout_test timeout_test.cpp)
-target_compile_definitions(discovery_timeout_test PRIVATE discoverySleepSec=10)
-gtest_discover_tests(
-  discovery_timeout_test
-  TEST_PREFIX discovery_
-  DISCOVERY_TIMEOUT 2
-)
diff --git a/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryMultiConfig.cmake b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryMultiConfig.cmake
new file mode 100644
index 0000000..1919dc1
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryMultiConfig.cmake
@@ -0,0 +1,14 @@
+project(test_include_dirs)
+include(CTest)
+include(GoogleTest)
+
+enable_testing()
+
+add_executable(configuration_gtest configuration_gtest.cpp)
+target_compile_definitions(configuration_gtest PRIVATE $<$<CONFIG:Debug>:DEBUG=1>)
+
+gtest_discover_tests(
+  configuration_gtest
+  PROPERTIES LABELS CONFIG
+  DISCOVERY_MODE PRE_TEST
+)
diff --git a/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTimeout.cmake b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTimeout.cmake
new file mode 100644
index 0000000..7398faf
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTimeout.cmake
@@ -0,0 +1,14 @@
+project(test_include_dirs)
+include(CTest)
+include(GoogleTest)
+
+enable_testing()
+
+add_executable(discovery_timeout_test timeout_test.cpp)
+target_compile_definitions(discovery_timeout_test PRIVATE discoverySleepSec=10)
+gtest_discover_tests(
+  discovery_timeout_test
+  TEST_PREFIX discovery_
+  DISCOVERY_TIMEOUT 2
+  DISCOVERY_MODE ${DISCOVERY_MODE}
+)
diff --git a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
index 8070512..6b9d458 100644
--- a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
@@ -1,6 +1,6 @@
 include(RunCMake)
 
-function(run_GoogleTest)
+function(run_GoogleTest DISCOVERY_MODE)
   # Use a single build tree for a few tests without cleaning.
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-build)
   set(RunCMake_TEST_NO_CLEAN 1)
@@ -10,7 +10,7 @@
   file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
   file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
 
-  run_cmake(GoogleTest)
+  run_cmake_with_options(GoogleTest -DCMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE=${DISCOVERY_MODE})
 
   run_cmake_command(GoogleTest-build
     ${CMAKE_COMMAND}
@@ -26,15 +26,6 @@
     --target property_timeout_test
   )
 
-  set(RunCMake_TEST_OUTPUT_MERGE 1)
-  run_cmake_command(GoogleTest-discovery-timeout-build
-    ${CMAKE_COMMAND}
-    --build .
-    --config Debug
-    --target discovery_timeout_test
-  )
-  set(RunCMake_TEST_OUTPUT_MERGE 0)
-
   run_cmake_command(GoogleTest-test1
     ${CMAKE_CTEST_COMMAND}
     -C Debug
@@ -69,16 +60,9 @@
     -R property_timeout\\.case_with_discovery
     --no-label-summary
   )
-
-  run_cmake_command(GoogleTest-discovery-timeout-test
-    ${CMAKE_CTEST_COMMAND}
-    -C Debug
-    -R discovery_timeout_test
-    --no-label-summary
-  )
 endfunction()
 
-function(run_GoogleTestXML)
+function(run_GoogleTestXML DISCOVERY_MODE)
   # Use a single build tree for a few tests without cleaning.
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTestXML-build)
   set(RunCMake_TEST_NO_CLEAN 1)
@@ -88,7 +72,7 @@
   file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
   file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
 
-  run_cmake(GoogleTestXML)
+  run_cmake_with_options(GoogleTestXML -DCMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE=${DISCOVERY_MODE})
 
   run_cmake_command(GoogleTestXML-discovery
   ${CMAKE_COMMAND}
@@ -105,5 +89,80 @@
   )
 endfunction()
 
-run_GoogleTest()
-run_GoogleTestXML()
+function(run_GoogleTest_discovery_timeout DISCOVERY_MODE)
+  # Use a single build tree for a few tests without cleaning.
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-discovery-timeout)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
+  endif()
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+
+  run_cmake_with_options(GoogleTestDiscoveryTimeout -DDISCOVERY_MODE=${DISCOVERY_MODE})
+
+  set(RunCMake_TEST_OUTPUT_MERGE 1)
+  run_cmake_command(GoogleTest-discovery-${DISCOVERY_MODE}-timeout-build
+    ${CMAKE_COMMAND}
+    --build .
+    --config Debug
+    --target discovery_timeout_test
+  )
+  set(RunCMake_TEST_OUTPUT_MERGE 0)
+
+  run_cmake_command(GoogleTest-discovery-${DISCOVERY_MODE}-timeout-test
+    ${CMAKE_CTEST_COMMAND}
+    -C Debug
+    -R discovery_timeout_test
+    --no-label-sumary
+  )
+endfunction()
+
+function(run_GoogleTest_discovery_multi_config)
+  # Use a single build tree for a few tests without cleaning.
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-discovery-multi-config)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+
+  run_cmake(GoogleTestDiscoveryMultiConfig)
+
+  run_cmake_command(GoogleTest-build-release
+    ${CMAKE_COMMAND}
+    --build .
+    --config Release
+    --target configuration_gtest
+  )
+  run_cmake_command(GoogleTest-build-debug
+    ${CMAKE_COMMAND}
+    --build .
+    --config Debug
+    --target configuration_gtest
+  )
+  run_cmake_command(GoogleTest-configuration-release
+    ${CMAKE_CTEST_COMMAND}
+    -C Release
+    -L CONFIG
+    -N
+  )
+  run_cmake_command(GoogleTest-configuration-debug
+    ${CMAKE_CTEST_COMMAND}
+    -C Debug
+    -L CONFIG
+    -N
+  )
+
+endfunction()
+
+foreach(DISCOVERY_MODE POST_BUILD PRE_TEST)
+  message("Testing ${DISCOVERY_MODE} discovery mode via CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE global override...")
+  run_GoogleTest(${DISCOVERY_MODE})
+  run_GoogleTestXML(${DISCOVERY_MODE})
+  message("Testing ${DISCOVERY_MODE} discovery mode via DISCOVERY_MODE option...")
+  run_GoogleTest_discovery_timeout(${DISCOVERY_MODE})
+endforeach()
+
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  message("Testing PRE_TEST discovery multi configuration...")
+  run_GoogleTest_discovery_multi_config()
+endif()
diff --git a/Tests/RunCMake/GoogleTest/configuration_gtest.cpp b/Tests/RunCMake/GoogleTest/configuration_gtest.cpp
new file mode 100644
index 0000000..3cbb134
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/configuration_gtest.cpp
@@ -0,0 +1,23 @@
+#include <iostream>
+#include <string>
+
+int main(int argc, char** argv)
+{
+  // Note: GoogleTest.cmake doesn't actually depend on Google Test as such;
+  // it only requires that we produces output in the expected format when
+  // invoked with --gtest_list_tests. Thus, we fake that here. This allows us
+  // to test the module without actually needing Google Test.
+  if (argc > 1 && std::string(argv[1]) == "--gtest_list_tests") {
+    std::cout << "configuration." << std::endl;
+#ifdef DEBUG
+    std::cout << "  DISABLED_case_release" << std::endl;
+    std::cout << "  case_debug" << std::endl;
+#else
+    std::cout << "  case_release" << std::endl;
+    std::cout << "  DISABLED_case_debug" << std::endl;
+#endif
+    return 0;
+  }
+
+  return 1;
+}
diff --git a/Tests/RunCMake/GoogleTest/timeout_test.cpp b/Tests/RunCMake/GoogleTest/timeout_test.cpp
index b8ad055..5506269 100644
--- a/Tests/RunCMake/GoogleTest/timeout_test.cpp
+++ b/Tests/RunCMake/GoogleTest/timeout_test.cpp
@@ -4,9 +4,10 @@
 #  include <unistd.h>
 #endif
 
-#include <iostream>
 #include <string>
 
+#include <stdio.h>
+
 void sleepFor(unsigned seconds)
 {
 #if defined(_WIN32)
@@ -23,8 +24,8 @@
   // invoked with --gtest_list_tests. Thus, we fake that here. This allows us
   // to test the module without actually needing Google Test.
   if (argc > 1 && std::string(argv[1]) == "--gtest_list_tests") {
-    std::cout << "timeout." << std::endl;
-    std::cout << "  case" << std::endl;
+    printf("timeout.\n  case\n");
+    fflush(stdout);
 #ifdef discoverySleepSec
     sleepFor(discoverySleepSec);
 #endif
diff --git a/Tests/RunCMake/PrecompileHeaders/PchWarnInvalid-check.cmake b/Tests/RunCMake/PrecompileHeaders/PchWarnInvalid-check.cmake
new file mode 100644
index 0000000..3e7fb30
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/PchWarnInvalid-check.cmake
@@ -0,0 +1,22 @@
+if (NOT CMAKE_C_COMPILER_ID MATCHES "GNU|Intel" OR
+   (CMAKE_C_COMPILER_ID STREQUAL "Intel" AND CMAKE_HOST_WIN32))
+  return()
+endif()
+
+file(STRINGS ${RunCMake_TEST_BINARY_DIR}/compile_commands.json empty_dir_commands
+     REGEX "command.*-Winvalid-pch.*empty.dir/cmake_pch.h")
+file(STRINGS ${RunCMake_TEST_BINARY_DIR}/compile_commands.json foo_dir_commands
+     REGEX "command.*-Winvalid-pch.*foo.dir/cmake_pch.h")
+
+list(LENGTH empty_dir_commands empty_dir_commands_size)
+list(LENGTH foo_dir_commands foo_dir_commands_size)
+
+if (empty_dir_commands_size EQUAL 0)
+  set(RunCMake_TEST_FAILED "empty target should have -Winvalid-pch compile option present")
+  return()
+endif()
+
+if (foo_dir_commands_size GREATER 0)
+  set(RunCMake_TEST_FAILED "foo target should not have -Winvalid-pch compile option present")
+  return()
+endif()
diff --git a/Tests/RunCMake/PrecompileHeaders/PchWarnInvalid.cmake b/Tests/RunCMake/PrecompileHeaders/PchWarnInvalid.cmake
new file mode 100644
index 0000000..4525664
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/PchWarnInvalid.cmake
@@ -0,0 +1,16 @@
+enable_language(C)
+
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+add_library(empty empty.c)
+target_precompile_headers(empty PUBLIC
+  <stdio.h>
+  <string.h>
+)
+
+add_library(foo foo.c)
+target_precompile_headers(foo PUBLIC
+  <stdio.h>
+  <string.h>
+)
+set_target_properties(foo PROPERTIES PCH_WARN_INVALID OFF)
diff --git a/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake b/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
index f587c7d..3f684fc 100644
--- a/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
+++ b/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
@@ -21,3 +21,6 @@
 run_test(PchReuseFromPrefixed)
 run_test(PchReuseFromSubdir)
 run_cmake(PchMultilanguage)
+if(RunCMake_GENERATOR MATCHES "Make|Ninja")
+  run_cmake(PchWarnInvalid)
+endif()
diff --git a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
index 3ca7cc0..5ccca01 100644
--- a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
+++ b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
@@ -31,6 +31,8 @@
 run_cmake(VsPrecompileHeaders)
 run_cmake(VsPrecompileHeadersReuseFromCompilePDBName)
 run_cmake(VsDeployEnabled)
+run_cmake(VsSettings)
+run_cmake(VsSourceSettingsTool)
 
 run_cmake(VsWinRTByDefault)
 
diff --git a/Tests/RunCMake/VS10Project/VsSettings-check.cmake b/Tests/RunCMake/VS10Project/VsSettings-check.cmake
new file mode 100644
index 0000000..0f8b26c
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsSettings-check.cmake
@@ -0,0 +1,23 @@
+macro(ensure_props_set projectFile)
+  if(NOT EXISTS "${projectFile}")
+    set(RunCMake_TEST_FAILED "Project file ${projectFile} does not exist.")
+    return()
+  endif()
+
+  set(SettingFound FALSE)
+
+  file(STRINGS "${projectFile}" lines)
+  foreach(line IN LISTS lines)
+    if(line MATCHES "<SourceProperty1.*Debug.*>SourceProperty1Value</SourceProperty1>")
+      message("SourceProperty1 setting found")
+      set(SettingFound TRUE)
+    endif()
+  endforeach()
+
+  if (NOT SettingFound)
+    set(RunCMake_TEST_FAILED "SourceProperty1 setting was not found")
+    return()
+  endif()
+endmacro()
+
+ensure_props_set("${RunCMake_TEST_BINARY_DIR}/foo.vcxproj")
diff --git a/Tests/RunCMake/VS10Project/VsSettings.cmake b/Tests/RunCMake/VS10Project/VsSettings.cmake
new file mode 100644
index 0000000..a4b321b
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsSettings.cmake
@@ -0,0 +1,5 @@
+enable_language(CXX)
+
+add_library(foo foo.cpp shader.hlsl)
+set_property(SOURCE shader.hlsl PROPERTY VS_SETTINGS
+  "$<$<CONFIG:DEBUG>:SourceProperty1=SourceProperty1Value>")
diff --git a/Tests/RunCMake/VS10Project/VsSourceSettingsTool-check.cmake b/Tests/RunCMake/VS10Project/VsSourceSettingsTool-check.cmake
new file mode 100644
index 0000000..29a89c3
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsSourceSettingsTool-check.cmake
@@ -0,0 +1,34 @@
+macro(ensure_props_set projectFile)
+  if(NOT EXISTS "${projectFile}")
+    set(RunCMake_TEST_FAILED "Project file ${projectFile} does not exist.")
+    return()
+  endif()
+
+  set(FirstSettingFound FALSE)
+  set(SecondSettingFound FALSE)
+
+  file(STRINGS "${projectFile}" lines)
+  foreach(line IN LISTS lines)
+    if(line MATCHES "<TargetProperty1.*Debug.*>TargetProperty1ValueDebug</TargetProperty1>")
+      if (FirstSettingFound)
+        message("TargetProperty1 setting found twice")
+        set(SecondSettingFound TRUE)
+      else()
+        message("TargetProperty1 setting found once")
+        set(FirstSettingFound TRUE)
+      endif()
+    endif()
+  endforeach()
+
+  if (NOT FirstSettingFound)
+    set(RunCMake_TEST_FAILED "TargetProperty1 setting not found at all")
+    return()
+  endif()
+
+  if (NOT SecondSettingFound)
+    set(RunCMake_TEST_FAILED "TargetProperty1 setting found once when it should be found twice")
+    return()
+  endif()
+endmacro()
+
+ensure_props_set("${RunCMake_TEST_BINARY_DIR}/foo.vcxproj")
diff --git a/Tests/RunCMake/VS10Project/VsSourceSettingsTool.cmake b/Tests/RunCMake/VS10Project/VsSourceSettingsTool.cmake
new file mode 100644
index 0000000..498962f
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsSourceSettingsTool.cmake
@@ -0,0 +1,5 @@
+enable_language(CXX)
+
+add_library(foo foo.cpp shader.hlsl shader2.hlsl)
+set_property(TARGET foo PROPERTY VS_SOURCE_SETTINGS_FXCompile
+  "$<$<CONFIG:DEBUG>:TargetProperty1=TargetProperty1ValueDebug>")
diff --git a/Tests/RunCMake/VS10Project/shader.hlsl b/Tests/RunCMake/VS10Project/shader.hlsl
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/shader.hlsl
@@ -0,0 +1 @@
+
diff --git a/Tests/RunCMake/VS10Project/shader2.hlsl b/Tests/RunCMake/VS10Project/shader2.hlsl
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/shader2.hlsl
@@ -0,0 +1 @@
+
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 @@
 
 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)
diff --git a/Tests/RunCMake/target_link_libraries/ConfigCase-result.txt b/Tests/RunCMake/target_link_libraries/ConfigCase-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries/ConfigCase-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/target_link_libraries/ConfigCase-stderr.txt b/Tests/RunCMake/target_link_libraries/ConfigCase-stderr.txt
new file mode 100644
index 0000000..953c972
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries/ConfigCase-stderr.txt
@@ -0,0 +1,13 @@
+^CMake Error at ConfigCase.cmake:[0-9]+ \(add_library\):
+  Target "impl" links to target "config::impl-Debug" but the target was not
+  found.  Perhaps a find_package\(\) call is missing for an IMPORTED target, or
+  an ALIAS target is missing\?
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at ConfigCase.cmake:[0-9]+ \(add_library\):
+  Target "impl" links to target "config::iface-Debug" but the target was not
+  found.  Perhaps a find_package\(\) call is missing for an IMPORTED target, or
+  an ALIAS target is missing\?
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/target_link_libraries/ConfigCase.cmake b/Tests/RunCMake/target_link_libraries/ConfigCase.cmake
new file mode 100644
index 0000000..fc39478
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries/ConfigCase.cmake
@@ -0,0 +1,6 @@
+cmake_policy(VERSION 3.15)
+enable_language(C)
+add_library(iface INTERFACE)
+target_link_libraries(iface INTERFACE "config::iface-$<CONFIG>")
+add_library(impl empty.c)
+target_link_libraries(impl PRIVATE "config::impl-$<CONFIG>" iface)
diff --git a/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake b/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake
index c7feb5f..fb223ab 100644
--- a/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake
+++ b/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake
@@ -1,5 +1,13 @@
 include(RunCMake)
 
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Debug)
+else()
+  set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
+endif()
+run_cmake(ConfigCase)
+unset(RunCMake_TEST_OPTIONS)
+
 run_cmake(CMP0023-WARN)
 run_cmake(CMP0023-NEW)
 run_cmake(CMP0023-WARN-2)
diff --git a/Tests/VSWinStorePhone/CMakeLists.txt b/Tests/VSWinStorePhone/CMakeLists.txt
index b8e157d..558d5de 100644
--- a/Tests/VSWinStorePhone/CMakeLists.txt
+++ b/Tests/VSWinStorePhone/CMakeLists.txt
@@ -127,7 +127,7 @@
 set_property(SOURCE ${VERTEXSHADER_FILES} PROPERTY VS_SHADER_MODEL 4.0_level_9_3)
 set_property(SOURCE ${VERTEXSHADER_FILES} PROPERTY VS_SHADER_FLAGS "/DFLAGS_ADDED")
 set_property(SOURCE ${VERTEXSHADER_FILES} PROPERTY VS_SHADER_OUTPUT_HEADER_FILE "$(OutDir)%(Filename).h")
-
+set_property(SOURCE ${VERTEXSHADER_FILES} PROPERTY VS_SETTINGS "$<$<CONFIG:DEBUG>:SourceProperty1=SourceProperty1Value>")
 
 source_group("Source Files" FILES ${SOURCE_FILES})
 source_group("Header Files" FILES ${HEADER_FILES})
@@ -135,6 +135,11 @@
 
 add_executable(${EXE_NAME} WIN32 ${SOURCE_FILES} ${HEADER_FILES} ${RESOURCE_FILES})
 set_property(TARGET ${EXE_NAME} PROPERTY VS_WINRT_COMPONENT TRUE)
+set_property(TARGET ${EXE_NAME} PROPERTY VS_SOURCE_SETTINGS_FXCompile
+  "TargetProperty1=$<$<CONFIG:DEBUG>:TargetProperty1ValueDebug>$<$<CONFIG:RELEASE>:TargetProperty1ValueRelease>")
+
+add_custom_command(TARGET ${EXE_NAME} POST_BUILD
+  COMMAND ${CMAKE_COMMAND} -Dvcxproj="${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.vcxproj" -P "${CMAKE_CURRENT_SOURCE_DIR}/EnsurePropertiesSet.cmake")
 
 string(SUBSTRING "${CMAKE_SYSTEM_VERSION}" 0, 4, SHORT_VERSION)
 
diff --git a/Tests/VSWinStorePhone/EnsurePropertiesSet.cmake b/Tests/VSWinStorePhone/EnsurePropertiesSet.cmake
new file mode 100644
index 0000000..528c46f
--- /dev/null
+++ b/Tests/VSWinStorePhone/EnsurePropertiesSet.cmake
@@ -0,0 +1,45 @@
+macro(ensure_props_set projectFile)
+  if(NOT EXISTS "${projectFile}")
+    message(FATAL_ERROR "Project file ${projectFile} does not exist.")
+    return()
+  endif()
+
+  set(SourcePropertyFound FALSE)
+  set(DebugTargetPropertyFound FALSE)
+  set(ReleaseTargetPropertyFound FALSE)
+
+  file(STRINGS "${projectFile}" lines)
+  foreach(line IN LISTS lines)
+    if(line MATCHES "<SourceProperty1.*Debug.*>SourceProperty1Value</SourceProperty1>")
+      message("SourceProperty1 setting found")
+      set(SourcePropertyFound TRUE)
+    endif()
+
+    if(line MATCHES "<TargetProperty1.*Debug.*>TargetProperty1ValueDebug</TargetProperty1>")
+      message("Debug TargetProperty1 setting found")
+      set(DebugTargetPropertyFound TRUE)
+    endif()
+
+    if(line MATCHES "<TargetProperty1.*Release.*>TargetProperty1ValueRelease</TargetProperty1>")
+      message("Release TargetProperty1 setting found")
+      set(ReleaseTargetPropertyFound TRUE)
+    endif()
+  endforeach()
+
+  if (NOT SourcePropertyFound)
+    message(FATAL_ERROR "SourceProperty1 setting not found")
+    return()
+  endif()
+
+  if (NOT DebugTargetPropertyFound)
+    message(FATAL_ERROR "Debug TargetProperty1 setting not found")
+    return()
+  endif()
+
+  if (NOT ReleaseTargetPropertyFound)
+    message(FATAL_ERROR "Release TargetProperty1 setting not found")
+    return()
+  endif()
+endmacro()
+
+ensure_props_set("${vcxproj}")