Merge topic 'escape-install-rpath'

4caefbb423 cmInstallTargetGenerator: Add tests for the RPATH_CHANGE rule
749ce48eb5 cmInstallTargetGenerator: Escape generated OLD_RPATH argument
9e84c7c5e8 cmInstallTargetGenerator: Introduce CMP0095

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !3383
diff --git a/Help/command/add_test.rst b/Help/command/add_test.rst
index 46b9b63..884b2ee 100644
--- a/Help/command/add_test.rst
+++ b/Help/command/add_test.rst
@@ -7,7 +7,8 @@
 
   add_test(NAME <name> COMMAND <command> [<arg>...]
            [CONFIGURATIONS <config>...]
-           [WORKING_DIRECTORY <dir>])
+           [WORKING_DIRECTORY <dir>]
+           [COMMAND_EXPAND_LISTS])
 
 Adds a test called ``<name>``.  The test name may not contain spaces,
 quotes, or other characters special in CMake syntax.  The options are:
@@ -28,6 +29,11 @@
   directory set to the build directory corresponding to the
   current source directory.
 
+``COMMAND_EXPAND_LISTS``
+  Lists in ``COMMAND`` arguments will be expanded, including those
+  created with
+  :manual:`generator expressions <cmake-generator-expressions(7)>`.
+
 The given test command is expected to exit with code ``0`` to pass and
 non-zero to fail, or vice-versa if the :prop_test:`WILL_FAIL` test
 property is set.  Any output written to stdout or stderr will be
diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst
index f3e0208..04b6ed2 100644
--- a/Help/manual/cmake-file-api.7.rst
+++ b/Help/manual/cmake-file-api.7.rst
@@ -379,6 +379,8 @@
 a new reply.  The client may simply start again by reading the new
 reply index file.
 
+.. _`file-api object kinds`:
+
 Object Kinds
 ============
 
diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst
index f1d02eb..13cba71 100644
--- a/Help/manual/cmake.1.rst
+++ b/Help/manual/cmake.1.rst
@@ -423,6 +423,22 @@
       A list of strings with all the extra generators compatible with
       the generator.
 
+  ``fileApi``
+    Optional member that is present when the :manual:`cmake-file-api(7)`
+    is available.  The value is a JSON object with one member:
+
+    ``requests``
+      A JSON array containing zero or more supported file-api requests.
+      Each request is a JSON object with members:
+
+      ``kind``
+        Specifies one of the supported :ref:`file-api object kinds`.
+
+      ``version``
+        A JSON array whose elements are each a JSON object containing
+        ``major`` and ``minor`` members specifying non-negative integer
+        version components.
+
   ``serverMode``
     ``true`` if cmake supports server-mode and ``false`` otherwise.
 
diff --git a/Help/release/dev/add_test-expand_lists.rst b/Help/release/dev/add_test-expand_lists.rst
new file mode 100644
index 0000000..88d26b7
--- /dev/null
+++ b/Help/release/dev/add_test-expand_lists.rst
@@ -0,0 +1,6 @@
+add_test-expand_lists
+---------------------
+
+* The command :command:`add_test` learned the option ``COMMAND_EXPAND_LISTS``
+  which causes lists in the ``COMMAND`` argument to be expanded, including
+  lists created by generator expressions.
diff --git a/Modules/CMakeSwiftInformation.cmake b/Modules/CMakeSwiftInformation.cmake
index 21f18d4..6f99c1f 100644
--- a/Modules/CMakeSwiftInformation.cmake
+++ b/Modules/CMakeSwiftInformation.cmake
@@ -23,6 +23,8 @@
 set(CMAKE_Swift_COMPILER_ARG1 -frontend)
 set(CMAKE_Swift_DEFINE_FLAG -D)
 set(CMAKE_Swift_FRAMEWORK_SEARCH_FLAG "-F ")
+set(CMAKE_Swift_LIBRARY_PATH_FLAG "-L ")
+set(CMAKE_Swift_LIBRARY_PATH_TERMINATOR "")
 set(CMAKE_Swift_LINKER_WRAPPER_FLAG "-Xlinker" " ")
 set(CMAKE_Swift_RESPONSE_FILE_LINK_FLAG @)
 
diff --git a/Modules/CheckCXXSymbolExists.cmake b/Modules/CheckCXXSymbolExists.cmake
index 970e301..2cccd09 100644
--- a/Modules/CheckCXXSymbolExists.cmake
+++ b/Modules/CheckCXXSymbolExists.cmake
@@ -24,7 +24,7 @@
   as a function or variable then the symbol must also be available for
   linking.  If the symbol is a type or enum value it will not be
   recognized (consider using :module:`CheckTypeSize`
-  or :module:`CheckCSourceCompiles`).
+  or :module:`CheckCXXSourceCompiles`).
 
 The following variables may be set before calling this macro to modify
 the way the check is run:
diff --git a/Modules/FindBLAS.cmake b/Modules/FindBLAS.cmake
index 65e5d1c..77f9d0e 100644
--- a/Modules/FindBLAS.cmake
+++ b/Modules/FindBLAS.cmake
@@ -152,7 +152,9 @@
 
   foreach(_library ${_list})
     set(_combined_name ${_combined_name}_${_library})
-
+    if(NOT "${_thread}" STREQUAL "")
+      set(_combined_name ${_combined_name}_thread)
+    endif()
     if(_libraries_work)
       if (BLA_STATIC)
         if (WIN32)
@@ -479,6 +481,18 @@
       ""
       )
   endif()
+  if(NOT BLAS_LIBRARIES)
+    find_package(Threads)
+    # OpenBLAS (http://www.openblas.net)
+    check_fortran_libraries(
+      BLAS_LIBRARIES
+      BLAS
+      sgemm
+      ""
+      "openblas"
+      "${CMAKE_THREAD_LIBS_INIT}"
+      )
+  endif()
 endif ()
 
 if (BLA_VENDOR STREQUAL "FLAME" OR BLA_VENDOR STREQUAL "All")
diff --git a/Modules/Internal/CPack/CPackDeb.cmake b/Modules/Internal/CPack/CPackDeb.cmake
index 89dc6f0..7791822 100644
--- a/Modules/Internal/CPack/CPackDeb.cmake
+++ b/Modules/Internal/CPack/CPackDeb.cmake
@@ -45,7 +45,7 @@
       ERROR_QUIET
       OUTPUT_STRIP_TRAILING_WHITESPACE)
     if(result EQUAL 0)
-      string(REGEX MATCH "\\(SONAME\\)[^\n]*\\[([^\n]+)\\.so\\.([^\n]*)\\]" soname "${output}")
+      string(REGEX MATCH "\\(?SONAME\\)?[^\n]*\\[([^\n]+)\\.so\\.([^\n]*)\\]" soname "${output}")
       set(${libname} "${CMAKE_MATCH_1}" PARENT_SCOPE)
       set(${version} "${CMAKE_MATCH_2}" PARENT_SCOPE)
     else()
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index af3a283..0316532 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -364,7 +364,6 @@
   cmProcessOutput.h
   cmProcessTools.cxx
   cmProcessTools.h
-  cmProperty.cxx
   cmProperty.h
   cmPropertyDefinition.cxx
   cmPropertyDefinition.h
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 55e4455..89858aa 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,5 +1,5 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 3)
 set(CMake_VERSION_MINOR 15)
-set(CMake_VERSION_PATCH 20190612)
+set(CMake_VERSION_PATCH 20190617)
 #set(CMake_VERSION_RC 1)
diff --git a/Source/QtDialog/CMakeSetupDialog.cxx b/Source/QtDialog/CMakeSetupDialog.cxx
index 5f6ccca..e98cdcf 100644
--- a/Source/QtDialog/CMakeSetupDialog.cxx
+++ b/Source/QtDialog/CMakeSetupDialog.cxx
@@ -862,7 +862,7 @@
     "built using Qt %2 (qt-project.org).\n"
 #ifdef USE_LGPL
     "\n"
-    "The Qt Toolkit is Copyright (C) Digia Plc and/or its subsidiary(-ies).\n"
+    "The Qt Toolkit is Copyright (C) The Qt Company Ltd.\n"
     "Qt is licensed under terms of the GNU LGPLv" USE_LGPL ", available at:\n"
     " \"%3\""
 #endif
diff --git a/Source/cmAddTestCommand.cxx b/Source/cmAddTestCommand.cxx
index bf28702..b0c462b 100644
--- a/Source/cmAddTestCommand.cxx
+++ b/Source/cmAddTestCommand.cxx
@@ -58,6 +58,7 @@
   std::vector<std::string> configurations;
   std::string working_directory;
   std::vector<std::string> command;
+  bool command_expand_lists = false;
 
   // Read the arguments.
   enum Doing
@@ -88,6 +89,13 @@
         return false;
       }
       doing = DoingWorkingDirectory;
+    } else if (args[i] == "COMMAND_EXPAND_LISTS") {
+      if (command_expand_lists) {
+        this->SetError(" may be given at most one COMMAND_EXPAND_LISTS.");
+        return false;
+      }
+      command_expand_lists = true;
+      doing = DoingNone;
     } else if (doing == DoingName) {
       name = args[i];
       doing = DoingNone;
@@ -134,6 +142,7 @@
   if (!working_directory.empty()) {
     test->SetProperty("WORKING_DIRECTORY", working_directory.c_str());
   }
+  test->SetCommandExpandLists(command_expand_lists);
   this->Makefile->AddTestGenerator(new cmTestGenerator(test, configurations));
 
   return true;
diff --git a/Source/cmCacheManager.cxx b/Source/cmCacheManager.cxx
index 358f095..e8fc350 100644
--- a/Source/cmCacheManager.cxx
+++ b/Source/cmCacheManager.cxx
@@ -620,7 +620,7 @@
 
 std::vector<std::string> cmCacheManager::CacheEntry::GetPropertyList() const
 {
-  return this->Properties.GetPropertyList();
+  return this->Properties.GetKeys();
 }
 
 const char* cmCacheManager::CacheEntry::GetProperty(
diff --git a/Source/cmDefinitions.cxx b/Source/cmDefinitions.cxx
index 5fafaf9..894447c 100644
--- a/Source/cmDefinitions.cxx
+++ b/Source/cmDefinitions.cxx
@@ -57,8 +57,7 @@
 
 void cmDefinitions::Set(const std::string& key, const char* value)
 {
-  Def def(value);
-  this->Map[key] = def;
+  this->Map[key] = Def(value);
 }
 
 std::vector<std::string> cmDefinitions::UnusedKeys() const
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index a12e0c4..c366183 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -12,7 +12,6 @@
 #include "cmMessageType.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmPropertyMap.h"
 #include "cmStateTypes.h"
 #include "cmSystemTools.h"
@@ -1205,12 +1204,9 @@
   std::string& errorMessage)
 {
   auto& targetProperties = gte->Target->GetProperties();
-  const auto& exportProperties = targetProperties.find("EXPORT_PROPERTIES");
-  if (exportProperties != targetProperties.end()) {
-    std::vector<std::string> propsToExport;
-    cmSystemTools::ExpandListArgument(exportProperties->second.GetValue(),
-                                      propsToExport);
-    for (auto& prop : propsToExport) {
+  if (const char* exportProperties =
+        targetProperties.GetPropertyValue("EXPORT_PROPERTIES")) {
+    for (auto& prop : cmSystemTools::ExpandedListArgument(exportProperties)) {
       /* Black list reserved properties */
       if (cmSystemTools::StringStartsWith(prop, "IMPORTED_") ||
           cmSystemTools::StringStartsWith(prop, "INTERFACE_")) {
diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx
index 34b6b33..ba42669 100644
--- a/Source/cmFileAPI.cxx
+++ b/Source/cmFileAPI.cxx
@@ -413,6 +413,14 @@
   return name;
 }
 
+Json::Value cmFileAPI::BuildVersion(unsigned int major, unsigned int minor)
+{
+  Json::Value version;
+  version["major"] = major;
+  version["minor"] = minor;
+  return version;
+}
+
 Json::Value cmFileAPI::BuildObject(Object const& object)
 {
   Json::Value value;
@@ -680,10 +688,9 @@
   Json::Value codemodel = cmFileAPICodemodelDump(*this, object.Version);
   codemodel["kind"] = this->ObjectKindName(object.Kind);
 
-  Json::Value& version = codemodel["version"] = Json::objectValue;
+  Json::Value& version = codemodel["version"];
   if (object.Version == 2) {
-    version["major"] = 2;
-    version["minor"] = CodeModelV2Minor;
+    version = BuildVersion(2, CodeModelV2Minor);
   } else {
     return codemodel; // should be unreachable
   }
@@ -716,10 +723,9 @@
   Json::Value cache = cmFileAPICacheDump(*this, object.Version);
   cache["kind"] = this->ObjectKindName(object.Kind);
 
-  Json::Value& version = cache["version"] = Json::objectValue;
+  Json::Value& version = cache["version"];
   if (object.Version == 2) {
-    version["major"] = 2;
-    version["minor"] = CacheV2Minor;
+    version = BuildVersion(2, CacheV2Minor);
   } else {
     return cache; // should be unreachable
   }
@@ -752,10 +758,9 @@
   Json::Value cmakeFiles = cmFileAPICMakeFilesDump(*this, object.Version);
   cmakeFiles["kind"] = this->ObjectKindName(object.Kind);
 
-  Json::Value& version = cmakeFiles["version"] = Json::objectValue;
+  Json::Value& version = cmakeFiles["version"];
   if (object.Version == 1) {
-    version["major"] = 1;
-    version["minor"] = CMakeFilesV1Minor;
+    version = BuildVersion(1, CMakeFilesV1Minor);
   } else {
     return cmakeFiles; // should be unreachable
   }
@@ -788,13 +793,43 @@
 {
   Json::Value test = Json::objectValue;
   test["kind"] = this->ObjectKindName(object.Kind);
-  Json::Value& version = test["version"] = Json::objectValue;
+  Json::Value& version = test["version"];
   if (object.Version == 2) {
-    version["major"] = 2;
-    version["minor"] = InternalTestV2Minor;
+    version = BuildVersion(2, InternalTestV2Minor);
   } else {
-    version["major"] = 1;
-    version["minor"] = InternalTestV1Minor;
+    version = BuildVersion(1, InternalTestV1Minor);
   }
   return test;
 }
+
+Json::Value cmFileAPI::ReportCapabilities()
+{
+  Json::Value capabilities = Json::objectValue;
+  Json::Value& requests = capabilities["requests"] = Json::arrayValue;
+
+  {
+    Json::Value request = Json::objectValue;
+    request["kind"] = ObjectKindName(ObjectKind::CodeModel);
+    Json::Value& versions = request["version"] = Json::arrayValue;
+    versions.append(BuildVersion(2, CodeModelV2Minor));
+    requests.append(std::move(request)); // NOLINT(*)
+  }
+
+  {
+    Json::Value request = Json::objectValue;
+    request["kind"] = ObjectKindName(ObjectKind::Cache);
+    Json::Value& versions = request["version"] = Json::arrayValue;
+    versions.append(BuildVersion(2, CacheV2Minor));
+    requests.append(std::move(request)); // NOLINT(*)
+  }
+
+  {
+    Json::Value request = Json::objectValue;
+    request["kind"] = ObjectKindName(ObjectKind::CMakeFiles);
+    Json::Value& versions = request["version"] = Json::arrayValue;
+    versions.append(BuildVersion(1, CMakeFilesV1Minor));
+    requests.append(std::move(request)); // NOLINT(*)
+  }
+
+  return capabilities;
+}
diff --git a/Source/cmFileAPI.h b/Source/cmFileAPI.h
index 341b072..602efa8 100644
--- a/Source/cmFileAPI.h
+++ b/Source/cmFileAPI.h
@@ -36,6 +36,9 @@
       and holding the original object.  Other JSON types are unchanged.  */
   Json::Value MaybeJsonFile(Json::Value in, std::string const& prefix);
 
+  /** Report file-api capabilities for cmake -E capabilities.  */
+  static Json::Value ReportCapabilities();
+
 private:
   cmake* CMakeInstance;
 
@@ -162,6 +165,8 @@
   static const char* ObjectKindName(ObjectKind kind);
   static std::string ObjectName(Object const& o);
 
+  static Json::Value BuildVersion(unsigned int major, unsigned int minor);
+
   Json::Value BuildObject(Object const& object);
 
   ClientRequests BuildClientRequests(Json::Value const& requests);
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index 8eefaa7..828488f 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -502,8 +502,13 @@
   if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_PREFER_CONFIG")) {
     if (this->UseConfigFiles && this->FindPackageUsingConfigMode()) {
       loadedPackage = true;
-    } else if (this->FindPackageUsingModuleMode()) {
-      loadedPackage = true;
+    } else {
+      if (this->FindPackageUsingModuleMode()) {
+        loadedPackage = true;
+      } else {
+        // The package was not loaded. Report errors.
+        HandlePackageMode(HandlePackageModeType::Module);
+      }
     }
   } else {
     if (this->UseFindModules && this->FindPackageUsingModuleMode()) {
@@ -603,7 +608,7 @@
   this->IgnoredPaths.insert(ignored.begin(), ignored.end());
 
   // Find and load the package.
-  return this->HandlePackageMode();
+  return this->HandlePackageMode(HandlePackageModeType::Config);
 }
 
 void cmFindPackageCommand::SetModuleVariables(const std::string& components)
@@ -722,7 +727,8 @@
   return true;
 }
 
-bool cmFindPackageCommand::HandlePackageMode()
+bool cmFindPackageCommand::HandlePackageMode(
+  HandlePackageModeType handlePackageModeType)
 {
   this->ConsideredConfigs.clear();
 
@@ -817,6 +823,12 @@
     }
   }
 
+  if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_PREFER_CONFIG") && !found &&
+      handlePackageModeType == HandlePackageModeType::Config) {
+    // Config mode failed. Allow Module case.
+    result = false;
+  }
+
   // package not found
   if (result && !found) {
     // warn if package required or neither quiet nor in config mode
diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h
index 4f6d97c..316ca0f 100644
--- a/Source/cmFindPackageCommand.h
+++ b/Source/cmFindPackageCommand.h
@@ -103,7 +103,14 @@
   bool FindModule(bool& found);
   void AddFindDefinition(const std::string& var, const char* val);
   void RestoreFindDefinitions();
-  bool HandlePackageMode();
+
+  enum /*class*/ HandlePackageModeType
+  {
+    Module,
+    Config
+  };
+  bool HandlePackageMode(HandlePackageModeType type);
+
   bool FindConfig();
   bool FindPrefixedConfig();
   bool FindFrameworkConfig();
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 036a07d..de0f371 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -5032,13 +5032,7 @@
 
 std::vector<std::string> cmGeneratorTarget::GetPropertyKeys() const
 {
-  cmPropertyMap const& propsObject = this->Target->GetProperties();
-  std::vector<std::string> props;
-  props.reserve(propsObject.size());
-  for (auto const& it : propsObject) {
-    props.push_back(it.first);
-  }
-  return props;
+  return this->Target->GetProperties().GetKeys();
 }
 
 void cmGeneratorTarget::ReportPropertyOrigin(
diff --git a/Source/cmJsonObjects.cxx b/Source/cmJsonObjects.cxx
index 636a8e1..8d065e1 100644
--- a/Source/cmJsonObjects.cxx
+++ b/Source/cmJsonObjects.cxx
@@ -14,7 +14,6 @@
 #include "cmLinkLineComputer.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmPropertyMap.h"
 #include "cmSourceFile.h"
 #include "cmState.h"
@@ -363,12 +362,12 @@
 
   // Build up the list of properties that may have been specified
   Json::Value properties = Json::arrayValue;
-  for (auto& prop : testInfo->GetProperties()) {
+  for (auto& prop : testInfo->GetProperties().GetList()) {
     Json::Value entry = Json::objectValue;
     entry[kKEY_KEY] = prop.first;
 
     // Remove config variables from the value too.
-    auto cge_value = ge.Parse(prop.second.GetValue());
+    auto cge_value = ge.Parse(prop.second);
     const std::string& processed_value = cge_value->Evaluate(lg, config);
     entry[kVALUE_KEY] = processed_value;
     properties.append(entry);
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index fe5c8af..3abf2dd 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -1444,10 +1444,23 @@
 
   std::string linkLanguage = cli.GetLinkLanguage();
 
-  const std::string& libPathFlag =
-    this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_FLAG");
-  const std::string& libPathTerminator =
-    this->Makefile->GetSafeDefinition("CMAKE_LIBRARY_PATH_TERMINATOR");
+  std::string libPathFlag;
+  if (const char* value = this->Makefile->GetDefinition(
+        "CMAKE_" + cli.GetLinkLanguage() + "_LIBRARY_PATH_FLAG")) {
+    libPathFlag = value;
+  } else {
+    libPathFlag =
+      this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_FLAG");
+  }
+
+  std::string libPathTerminator;
+  if (const char* value = this->Makefile->GetDefinition(
+        "CMAKE_" + cli.GetLinkLanguage() + "_LIBRARY_PATH_TERMINATOR")) {
+    libPathTerminator = value;
+  } else {
+    libPathTerminator =
+      this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_TERMINATOR");
+  }
 
   // Add standard libraries for this language.
   std::string standardLibsVar = "CMAKE_";
diff --git a/Source/cmProperty.cxx b/Source/cmProperty.cxx
deleted file mode 100644
index 27f0ecd..0000000
--- a/Source/cmProperty.cxx
+++ /dev/null
@@ -1,26 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmProperty.h"
-
-void cmProperty::Set(const char* value)
-{
-  this->Value = value;
-  this->ValueHasBeenSet = true;
-}
-
-void cmProperty::Append(const char* value, bool asString)
-{
-  if (!this->Value.empty() && *value && !asString) {
-    this->Value += ";";
-  }
-  this->Value += value;
-  this->ValueHasBeenSet = true;
-}
-
-const char* cmProperty::GetValue() const
-{
-  if (this->ValueHasBeenSet) {
-    return this->Value.c_str();
-  }
-  return nullptr;
-}
diff --git a/Source/cmProperty.h b/Source/cmProperty.h
index d11c5ef..80f131a 100644
--- a/Source/cmProperty.h
+++ b/Source/cmProperty.h
@@ -5,8 +5,6 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <string>
-
 class cmProperty
 {
 public:
@@ -22,22 +20,6 @@
     CACHED_VARIABLE,
     INSTALL
   };
-
-  // set this property
-  void Set(const char* value);
-
-  // append to this property
-  void Append(const char* value, bool asString = false);
-
-  // get the value
-  const char* GetValue() const;
-
-  // construct with the value not set
-  cmProperty() { this->ValueHasBeenSet = false; }
-
-protected:
-  std::string Value;
-  bool ValueHasBeenSet;
 };
 
 #endif
diff --git a/Source/cmPropertyMap.cxx b/Source/cmPropertyMap.cxx
index 3f6d7c8..3ed4c05 100644
--- a/Source/cmPropertyMap.cxx
+++ b/Source/cmPropertyMap.cxx
@@ -3,40 +3,21 @@
 #include "cmPropertyMap.h"
 
 #include <algorithm>
-#include <assert.h>
 #include <utility>
 
-cmProperty* cmPropertyMap::GetOrCreateProperty(const std::string& name)
+void cmPropertyMap::Clear()
 {
-  cmPropertyMap::iterator it = this->find(name);
-  cmProperty* prop;
-  if (it == this->end()) {
-    prop = &(*this)[name];
-  } else {
-    prop = &(it->second);
-  }
-  return prop;
-}
-
-std::vector<std::string> cmPropertyMap::GetPropertyList() const
-{
-  std::vector<std::string> keyList;
-  for (auto const& i : *this) {
-    keyList.push_back(i.first);
-  }
-  std::sort(keyList.begin(), keyList.end());
-  return keyList;
+  Map_.clear();
 }
 
 void cmPropertyMap::SetProperty(const std::string& name, const char* value)
 {
   if (!value) {
-    this->erase(name);
+    Map_.erase(name);
     return;
   }
 
-  cmProperty* prop = this->GetOrCreateProperty(name);
-  prop->Set(value);
+  Map_[name] = value;
 }
 
 void cmPropertyMap::AppendProperty(const std::string& name, const char* value,
@@ -47,17 +28,53 @@
     return;
   }
 
-  cmProperty* prop = this->GetOrCreateProperty(name);
-  prop->Append(value, asString);
+  {
+    std::string& pVal = Map_[name];
+    if (!pVal.empty() && !asString) {
+      pVal += ';';
+    }
+    pVal += value;
+  }
+}
+
+void cmPropertyMap::RemoveProperty(const std::string& name)
+{
+  Map_.erase(name);
 }
 
 const char* cmPropertyMap::GetPropertyValue(const std::string& name) const
 {
-  assert(!name.empty());
-
-  cmPropertyMap::const_iterator it = this->find(name);
-  if (it == this->end()) {
-    return nullptr;
+  {
+    auto it = Map_.find(name);
+    if (it != Map_.end()) {
+      return it->second.c_str();
+    }
   }
-  return it->second.GetValue();
+  return nullptr;
+}
+
+std::vector<std::string> cmPropertyMap::GetKeys() const
+{
+  std::vector<std::string> keyList;
+  keyList.reserve(Map_.size());
+  for (auto const& item : Map_) {
+    keyList.push_back(item.first);
+  }
+  std::sort(keyList.begin(), keyList.end());
+  return keyList;
+}
+
+std::vector<std::pair<std::string, std::string>> cmPropertyMap::GetList() const
+{
+  typedef std::pair<std::string, std::string> StringPair;
+  std::vector<StringPair> kvList;
+  kvList.reserve(Map_.size());
+  for (auto const& item : Map_) {
+    kvList.emplace_back(item.first, item.second);
+  }
+  std::sort(kvList.begin(), kvList.end(),
+            [](StringPair const& a, StringPair const& b) {
+              return a.first < b.first;
+            });
+  return kvList;
 }
diff --git a/Source/cmPropertyMap.h b/Source/cmPropertyMap.h
index 5a05150..9aed349 100644
--- a/Source/cmPropertyMap.h
+++ b/Source/cmPropertyMap.h
@@ -5,25 +5,47 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include "cmProperty.h"
-
-#include <map>
 #include <string>
+#include <unordered_map>
+#include <utility>
 #include <vector>
 
-class cmPropertyMap : public std::map<std::string, cmProperty>
+/** \class cmPropertyMap
+ * \brief String property map.
+ */
+class cmPropertyMap
 {
 public:
-  cmProperty* GetOrCreateProperty(const std::string& name);
+  // -- General
 
-  std::vector<std::string> GetPropertyList() const;
+  //! Clear property list
+  void Clear();
 
+  // -- Properties
+
+  //! Set the property value
   void SetProperty(const std::string& name, const char* value);
 
+  //! Append to the property value
   void AppendProperty(const std::string& name, const char* value,
                       bool asString = false);
 
+  //! Get the property value
   const char* GetPropertyValue(const std::string& name) const;
+
+  //! Remove the property @a name from the map
+  void RemoveProperty(const std::string& name);
+
+  // -- Lists
+
+  //! Get a sorted list of property keys
+  std::vector<std::string> GetKeys() const;
+
+  //! Get a sorted by key list of property key,value pairs
+  std::vector<std::pair<std::string, std::string>> GetList() const;
+
+private:
+  std::unordered_map<std::string, std::string> Map_;
 };
 
 #endif
diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx
index dad8821..558391f 100644
--- a/Source/cmServerProtocol.cxx
+++ b/Source/cmServerProtocol.cxx
@@ -630,7 +630,7 @@
   Json::Value obj = Json::objectValue;
 
   // Capabilities information:
-  obj[kCAPABILITIES_KEY] = cm->ReportCapabilitiesJson(true);
+  obj[kCAPABILITIES_KEY] = cm->ReportCapabilitiesJson();
 
   obj[kDEBUG_OUTPUT_KEY] = cm->GetDebugOutput();
   obj[kTRACE_KEY] = cm->GetTrace();
diff --git a/Source/cmState.cxx b/Source/cmState.cxx
index fa7df0b..091c2e0 100644
--- a/Source/cmState.cxx
+++ b/Source/cmState.cxx
@@ -267,7 +267,7 @@
 
 cmStateSnapshot cmState::Reset()
 {
-  this->GlobalProperties.clear();
+  this->GlobalProperties.Clear();
   this->PropertyDefinitions.clear();
   this->GlobVerificationManager->Reset();
 
@@ -289,7 +289,7 @@
     it->LinkDirectoriesBacktraces.clear();
     it->DirectoryEnd = pos;
     it->NormalTargetNames.clear();
-    it->Properties.clear();
+    it->Properties.Clear();
     it->Children.clear();
   }
 
diff --git a/Source/cmStateDirectory.cxx b/Source/cmStateDirectory.cxx
index 182d3fe..6ca1c9f 100644
--- a/Source/cmStateDirectory.cxx
+++ b/Source/cmStateDirectory.cxx
@@ -6,7 +6,6 @@
 #include <algorithm>
 #include <assert.h>
 #include <iterator>
-#include <utility>
 
 #include "cmAlgorithms.h"
 #include "cmProperty.h"
@@ -667,12 +666,7 @@
 
 std::vector<std::string> cmStateDirectory::GetPropertyKeys() const
 {
-  std::vector<std::string> keys;
-  keys.reserve(this->DirectoryState->Properties.size());
-  for (auto const& it : this->DirectoryState->Properties) {
-    keys.push_back(it.first);
-  }
-  return keys;
+  return this->DirectoryState->Properties.GetKeys();
 }
 
 void cmStateDirectory::AddNormalTargetName(std::string const& name)
diff --git a/Source/cmTest.cxx b/Source/cmTest.cxx
index 7d45cf5..01f2b96 100644
--- a/Source/cmTest.cxx
+++ b/Source/cmTest.cxx
@@ -8,7 +8,8 @@
 #include "cmSystemTools.h"
 
 cmTest::cmTest(cmMakefile* mf)
-  : Backtrace(mf->GetBacktrace())
+  : CommandExpandLists(false)
+  , Backtrace(mf->GetBacktrace())
 {
   this->Makefile = mf;
   this->OldStyle = true;
@@ -59,3 +60,13 @@
 {
   this->Properties.AppendProperty(prop, value, asString);
 }
+
+bool cmTest::GetCommandExpandLists() const
+{
+  return this->CommandExpandLists;
+}
+
+void cmTest::SetCommandExpandLists(bool b)
+{
+  this->CommandExpandLists = b;
+}
diff --git a/Source/cmTest.h b/Source/cmTest.h
index 88dc730..02d8f46 100644
--- a/Source/cmTest.h
+++ b/Source/cmTest.h
@@ -51,10 +51,15 @@
   bool GetOldStyle() const { return this->OldStyle; }
   void SetOldStyle(bool b) { this->OldStyle = b; }
 
+  /** Set/Get whether lists in command lines should be expanded. */
+  bool GetCommandExpandLists() const;
+  void SetCommandExpandLists(bool b);
+
 private:
   cmPropertyMap Properties;
   std::string Name;
   std::vector<std::string> Command;
+  bool CommandExpandLists;
 
   bool OldStyle;
 
diff --git a/Source/cmTestGenerator.cxx b/Source/cmTestGenerator.cxx
index 571cd09..498953e 100644
--- a/Source/cmTestGenerator.cxx
+++ b/Source/cmTestGenerator.cxx
@@ -10,7 +10,6 @@
 #include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
 #include "cmOutputConverter.h"
-#include "cmProperty.h"
 #include "cmPropertyMap.h"
 #include "cmRange.h"
 #include "cmStateTypes.h"
@@ -76,12 +75,22 @@
   // Start the test command.
   os << indent << "add_test(" << this->Test->GetName() << " ";
 
-  // Get the test command line to be executed.
-  std::vector<std::string> const& command = this->Test->GetCommand();
+  // Evaluate command line arguments
+  std::vector<std::string> argv =
+    EvaluateCommandLineArguments(this->Test->GetCommand(), ge, config);
+
+  // Expand arguments if COMMAND_EXPAND_LISTS is set
+  if (this->Test->GetCommandExpandLists()) {
+    argv = cmSystemTools::ExpandedLists(argv.begin(), argv.end());
+    // Expanding lists on an empty command may have left it empty
+    if (argv.empty()) {
+      argv.emplace_back();
+    }
+  }
 
   // Check whether the command executable is a target whose name is to
   // be translated.
-  std::string exe = command[0];
+  std::string exe = argv[0];
   cmGeneratorTarget* target = this->LG->FindGeneratorTargetToUse(exe);
   if (target && target->GetType() == cmStateEnums::EXECUTABLE) {
     // Use the target file on disk.
@@ -101,29 +110,26 @@
     }
   } else {
     // Use the command name given.
-    exe = ge.Parse(exe)->Evaluate(this->LG, config);
     cmSystemTools::ConvertToUnixSlashes(exe);
   }
 
   // Generate the command line with full escapes.
   os << cmOutputConverter::EscapeForCMake(exe);
-  for (std::string const& arg : cmMakeRange(command).advance(1)) {
-    os << " "
-       << cmOutputConverter::EscapeForCMake(
-            ge.Parse(arg)->Evaluate(this->LG, config));
+
+  for (auto const& arg : cmMakeRange(argv).advance(1)) {
+    os << " " << cmOutputConverter::EscapeForCMake(arg);
   }
 
   // Finish the test command.
   os << ")\n";
 
   // Output properties for the test.
-  cmPropertyMap& pm = this->Test->GetProperties();
   os << indent << "set_tests_properties(" << this->Test->GetName()
      << " PROPERTIES ";
-  for (auto const& i : pm) {
+  for (auto const& i : this->Test->GetProperties().GetList()) {
     os << " " << i.first << " "
        << cmOutputConverter::EscapeForCMake(
-            ge.Parse(i.second.GetValue())->Evaluate(this->LG, config));
+            ge.Parse(i.second)->Evaluate(this->LG, config));
   }
   this->GenerateInternalProperties(os);
   os << ")" << std::endl;
@@ -173,12 +179,11 @@
   fout << ")" << std::endl;
 
   // Output properties for the test.
-  cmPropertyMap& pm = this->Test->GetProperties();
   fout << indent << "set_tests_properties(" << this->Test->GetName()
        << " PROPERTIES ";
-  for (auto const& i : pm) {
+  for (auto const& i : this->Test->GetProperties().GetList()) {
     fout << " " << i.first << " "
-         << cmOutputConverter::EscapeForCMake(i.second.GetValue());
+         << cmOutputConverter::EscapeForCMake(i.second);
   }
   this->GenerateInternalProperties(fout);
   fout << ")" << std::endl;
@@ -208,3 +213,16 @@
 
   os << "\"";
 }
+
+std::vector<std::string> cmTestGenerator::EvaluateCommandLineArguments(
+  const std::vector<std::string>& argv, cmGeneratorExpression& ge,
+  const std::string& config) const
+{
+  // Evaluate executable name and arguments
+  auto evaluatedRange =
+    cmMakeRange(argv).transform([&](const std::string& arg) {
+      return ge.Parse(arg)->Evaluate(this->LG, config);
+    });
+
+  return { evaluatedRange.begin(), evaluatedRange.end() };
+}
diff --git a/Source/cmTestGenerator.h b/Source/cmTestGenerator.h
index 8b9cf78..7ac68eb 100644
--- a/Source/cmTestGenerator.h
+++ b/Source/cmTestGenerator.h
@@ -11,6 +11,7 @@
 #include <string>
 #include <vector>
 
+class cmGeneratorExpression;
 class cmLocalGenerator;
 class cmTest;
 
@@ -38,6 +39,9 @@
 
 private:
   void GenerateInternalProperties(std::ostream& os);
+  std::vector<std::string> EvaluateCommandLineArguments(
+    const std::vector<std::string>& argv, cmGeneratorExpression& ge,
+    const std::string& config) const;
 
 protected:
   void GenerateScriptConfigs(std::ostream& os, Indent indent) override;
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 9368414..634c990 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -774,11 +774,11 @@
     cmSystemTools::ExpandListArgument(vsDotNetReferences, references);
   }
   cmPropertyMap const& props = this->GeneratorTarget->Target->GetProperties();
-  for (auto const& i : props) {
+  for (auto const& i : props.GetList()) {
     if (i.first.find("VS_DOTNET_REFERENCE_") == 0) {
       std::string name = i.first.substr(20);
       if (!name.empty()) {
-        std::string path = i.second.GetValue();
+        std::string path = i.second;
         if (!cmsys::SystemTools::FileIsFullPath(path)) {
           path = this->Makefile->GetCurrentSourceDirectory() + "/" + path;
         }
@@ -870,10 +870,10 @@
   typedef std::map<std::string, std::string> CustomTags;
   CustomTags tags;
   cmPropertyMap const& props = this->GeneratorTarget->Target->GetProperties();
-  for (const auto& i : props) {
+  for (const auto& i : props.GetList()) {
     if (i.first.find(refPropFullPrefix) == 0) {
       std::string refTag = i.first.substr(refPropFullPrefix.length());
-      std::string refVal = i.second.GetValue();
+      std::string refVal = i.second;
       if (!refTag.empty() && !refVal.empty()) {
         tags[refTag] = refVal;
       }
@@ -967,12 +967,12 @@
           }
         }
         const cmPropertyMap& props = oi->GetProperties();
-        for (const auto& p : props) {
+        for (const std::string& p : props.GetKeys()) {
           static const std::string propNamePrefix = "VS_CSHARP_";
-          if (p.first.find(propNamePrefix) == 0) {
-            std::string tagName = p.first.substr(propNamePrefix.length());
+          if (p.find(propNamePrefix) == 0) {
+            std::string tagName = p.substr(propNamePrefix.length());
             if (!tagName.empty()) {
-              std::string value = props.GetPropertyValue(p.first);
+              std::string value = props.GetPropertyValue(p);
               if (!value.empty()) {
                 e2.Element(tagName.c_str(), value);
               }
@@ -4681,12 +4681,12 @@
 {
   if (this->ProjectType == csproj) {
     const cmPropertyMap& props = sf->GetProperties();
-    for (auto const& p : props) {
+    for (const std::string& p : props.GetKeys()) {
       static const std::string propNamePrefix = "VS_CSHARP_";
-      if (p.first.find(propNamePrefix) == 0) {
-        std::string tagName = p.first.substr(propNamePrefix.length());
+      if (p.find(propNamePrefix) == 0) {
+        std::string tagName = p.substr(propNamePrefix.length());
         if (!tagName.empty()) {
-          const std::string val = props.GetPropertyValue(p.first);
+          const std::string val = props.GetPropertyValue(p);
           if (!val.empty()) {
             tags[tagName] = val;
           } else {
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index f0b53f4..3772f09 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -248,7 +248,7 @@
   return version;
 }
 
-Json::Value cmake::ReportCapabilitiesJson(bool haveServerMode) const
+Json::Value cmake::ReportCapabilitiesJson() const
 {
   Json::Value obj = Json::objectValue;
 
@@ -284,18 +284,19 @@
     generators.append(i.second);
   }
   obj["generators"] = generators;
-  obj["serverMode"] = haveServerMode;
+  obj["fileApi"] = cmFileAPI::ReportCapabilities();
+  obj["serverMode"] = true;
 
   return obj;
 }
 #endif
 
-std::string cmake::ReportCapabilities(bool haveServerMode) const
+std::string cmake::ReportCapabilities() const
 {
   std::string result;
 #if defined(CMAKE_BUILD_WITH_CMAKE)
   Json::FastWriter writer;
-  result = writer.write(this->ReportCapabilitiesJson(haveServerMode));
+  result = writer.write(this->ReportCapabilitiesJson());
 #else
   result = "Not supported";
 #endif
diff --git a/Source/cmake.h b/Source/cmake.h
index 4de9d28..fa4409a 100644
--- a/Source/cmake.h
+++ b/Source/cmake.h
@@ -136,9 +136,9 @@
 
 #if defined(CMAKE_BUILD_WITH_CMAKE)
   Json::Value ReportVersionJson() const;
-  Json::Value ReportCapabilitiesJson(bool haveServerMode) const;
+  Json::Value ReportCapabilitiesJson() const;
 #endif
-  std::string ReportCapabilities(bool haveServerMode) const;
+  std::string ReportCapabilities() const;
 
   //@{
   /**
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index a983d30..86082e5 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -734,11 +734,7 @@
         return 1;
       }
       cmake cm(cmake::RoleInternal, cmState::Unknown);
-#if defined(CMAKE_BUILD_WITH_CMAKE)
-      std::cout << cm.ReportCapabilities(true);
-#else
-      std::cout << cm.ReportCapabilities(false);
-#endif
+      std::cout << cm.ReportCapabilities();
       return 0;
     }
 
diff --git a/Tests/FindPackageTest/CMakeLists.txt b/Tests/FindPackageTest/CMakeLists.txt
index 8802b73..affe5d5 100644
--- a/Tests/FindPackageTest/CMakeLists.txt
+++ b/Tests/FindPackageTest/CMakeLists.txt
@@ -579,3 +579,16 @@
 
 set(CMAKE_FIND_PACKAGE_PREFER_CONFIG OFF)
 set(CMAKE_PREFIX_PATH)
+
+############################################################################
+##Test find_package CMAKE_FIND_PACKAGE_PREFER_CONFIG with module fallback
+
+set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/PreferConfigOnlyModule)
+
+set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)
+
+find_package(ACME REQUIRED)
+
+if(NOT ACME_FOUND)
+    message(SEND_ERROR "Did not find ACME package")
+endif()
diff --git a/Tests/FindPackageTest/PreferConfigOnlyModule/FindACME.cmake b/Tests/FindPackageTest/PreferConfigOnlyModule/FindACME.cmake
new file mode 100644
index 0000000..7a4e1b3
--- /dev/null
+++ b/Tests/FindPackageTest/PreferConfigOnlyModule/FindACME.cmake
@@ -0,0 +1 @@
+set(ACME_FOUND TRUE)
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index d5ac7e0..735ad5f 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -570,3 +570,5 @@
   add_RunCMake_test(CSharpCustomCommand)
   add_RunCMake_test(CSharpReferenceImport)
 endif()
+
+add_RunCMake_test("CTestCommandExpandLists")
diff --git a/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt
new file mode 100644
index 0000000..3e470a2
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.14)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt.in b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt.in
new file mode 100644
index 0000000..7d56c90
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt.in
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.14)
+project(@CASE_NAME@ NONE)
+include("@RunCMake_SOURCE_DIR@/@CASE_NAME@.cmake")
diff --git a/Tests/RunCMake/CTestCommandExpandLists/RunCMakeTest.cmake b/Tests/RunCMake/CTestCommandExpandLists/RunCMakeTest.cmake
new file mode 100644
index 0000000..7c3779e
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/RunCMakeTest.cmake
@@ -0,0 +1,5 @@
+include(RunCTest)
+
+run_ctest(expandGeneratorExpressionResult)
+run_ctest(expandEmptyCommand)
+run_cmake(multipleExpandOptions)
diff --git a/Tests/RunCMake/CTestCommandExpandLists/compare_options.cmake b/Tests/RunCMake/CTestCommandExpandLists/compare_options.cmake
new file mode 100644
index 0000000..a32e579
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/compare_options.cmake
@@ -0,0 +1,14 @@
+set(range 1 2 3 4 5 6 7 8 9 10)
+set(aargs "")
+set(bargs "")
+foreach(n IN LISTS range)
+  set(aval "${A${n}ARG}")
+  set(bval "${B${n}ARG}")
+  if(aval OR bval)
+    list(APPEND aargs "\"${aval}\"")
+    list(APPEND bargs "\"${bval}\"")
+  endif()
+endforeach()
+if(NOT "${aargs}" STREQUAL "${bargs}")
+  message(FATAL_ERROR "COMPARE_OPTIONS: \n\t${aargs} != \n\t${bargs}")
+endif()
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-result.txt b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stderr.txt b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stderr.txt
new file mode 100644
index 0000000..c656b4c
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stderr.txt
@@ -0,0 +1 @@
+Unable to find executable:
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stdout.txt b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stdout.txt
new file mode 100644
index 0000000..0752580
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stdout.txt
@@ -0,0 +1,13 @@
+Test project .*/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-build
+.* +Start 1: CommandExpandEmptyList
+Could not find executable +
+Looked in the following places:
+.*
+1/1 Test #1: CommandExpandEmptyList +\.+\*\*\*Not Run +[0-9.]+ sec
++
+0% tests passed, 1 tests failed out of 1
++
+Total Test time \(real\) = +[0-9.]+ sec
++
+The following tests FAILED:
+.* +1 - CommandExpandEmptyList \(Not Run\)$
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand.cmake b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand.cmake
new file mode 100644
index 0000000..b75828e
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand.cmake
@@ -0,0 +1,10 @@
+include(CTest)
+
+set(argv /bin/true)
+list(POP_BACK argv)
+
+add_test(
+  NAME CommandExpandEmptyList
+  COMMAND "$<JOIN:${argv},;>"
+  COMMAND_EXPAND_LISTS
+)
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-result.txt b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-result.txt
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-result.txt
@@ -0,0 +1 @@
+0
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-stdout.txt b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-stdout.txt
new file mode 100644
index 0000000..2f21592
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-stdout.txt
@@ -0,0 +1,7 @@
+Test project .*/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-build
+.* +Start 1: CommandExpandList
+1/1 Test #1: CommandExpandList +\.+ +Passed +[0-9.]+ sec
++
+100% tests passed, 0 tests failed out of 1
++
+Total Test time \(real\) = +[0-9.]+ sec
diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult.cmake b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult.cmake
new file mode 100644
index 0000000..20608ae
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult.cmake
@@ -0,0 +1,19 @@
+include(CTest)
+
+
+set(cmp_args "1ARG=COMMAND_EXPAND_LISTS" "2ARG=test" "3ARG=outfile"
+  "4ARG=content")
+set(AARGS "")
+foreach(arg IN LISTS cmp_args)
+  list(APPEND AARGS "-DA${arg}")
+endforeach()
+
+
+
+add_test(
+  NAME CommandExpandList
+  COMMAND ${CMAKE_COMMAND} ${AARGS} -V
+  "-DB$<JOIN:${cmp_args},;-DB>"
+  "-P" "${CMAKE_CURRENT_LIST_DIR}/compare_options.cmake"
+  COMMAND_EXPAND_LISTS
+)
diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-result.txt b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stderr.txt b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stderr.txt
new file mode 100644
index 0000000..e48513f
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stderr.txt
@@ -0,0 +1,2 @@
+CMake Error at multipleExpandOptions\.cmake:3 \(add_test\):
+ +add_test may be given at most one COMMAND_EXPAND_LISTS\.
diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stdout.txt b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stdout.txt
new file mode 100644
index 0000000..55bb894
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stdout.txt
@@ -0,0 +1,2 @@
+-- Configuring incomplete, errors occurred!
+See also ".*/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-build/CMakeFiles/CMakeOutput\.log".
diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions.cmake b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions.cmake
new file mode 100644
index 0000000..dcf2dc4
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions.cmake
@@ -0,0 +1,8 @@
+include(CTest)
+
+add_test(
+  NAME MultipleExpandOptions
+  COMMAND /bin/true
+  COMMAND_EXPAND_LISTS
+  COMMAND_EXPAND_LISTS
+)
diff --git a/Tests/RunCMake/CTestCommandExpandLists/test.cmake.in b/Tests/RunCMake/CTestCommandExpandLists/test.cmake.in
new file mode 100644
index 0000000..d9a8ccb
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandExpandLists/test.cmake.in
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.14)
+
+set(CTEST_SITE                          "test-site")
+set(CTEST_BUILD_NAME                    "test-build-name")
+set(CTEST_SOURCE_DIRECTORY              "@RunCMake_BINARY_DIR@/@CASE_NAME@")
+set(CTEST_BINARY_DIRECTORY              "@RunCMake_BINARY_DIR@/@CASE_NAME@-build")
+set(CTEST_CMAKE_GENERATOR               "@RunCMake_GENERATOR@")
+set(CTEST_CMAKE_GENERATOR_PLATFORM      "@RunCMake_GENERATOR_PLATFORM@")
+set(CTEST_CMAKE_GENERATOR_TOOLSET       "@RunCMake_GENERATOR_TOOLSET@")
+set(CTEST_BUILD_CONFIGURATION           "$ENV{CMAKE_CONFIG_TYPE}")
+
+ctest_start(Experimental)
+ctest_configure()
+ctest_build()
+ctest_test()
diff --git a/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt
index 6c5ea44..b4b170e 100644
--- a/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt
+++ b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt
@@ -1 +1 @@
-^{.*}$
+^{"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":0}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":true,"version":{.*}}$
diff --git a/bootstrap b/bootstrap
index 5ccb6b3..c2194fe 100755
--- a/bootstrap
+++ b/bootstrap
@@ -401,7 +401,6 @@
   cmPolicies \
   cmProcessOutput \
   cmProjectCommand \
-  cmProperty \
   cmPropertyDefinition \
   cmPropertyDefinitionMap \
   cmPropertyMap \