Merge topic 'libarchive-wincrypt-boringssl'

5752bb41e3 libarchive: Avoid preprocessor symbol conflicts with boringssl on Windows

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !12019
diff --git a/.clang-tidy b/.clang-tidy
index ae0dc23..3a0fd82 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -90,4 +90,18 @@
     value: '0'
   - key:   modernize-use-auto.MinTypeNameLength
     value: '80'
+  - key:   readability-identifier-naming.ClassCase
+    value: 'CamelCase'
+  - key:   readability-identifier-naming.ClassIgnoredRegexp
+    value: 'cm[A-Z][A-Za-z]+|(const_)?iterator'
+  - key:   readability-identifier-naming.ClassMemberCase
+    value: 'CamelCase'
+  - key:   readability-identifier-naming.ClassMemberIgnoredRegexp
+    value: 'prop[A-Z_]+'
+  - key:   readability-identifier-naming.PrivateMemberCase
+    value: 'CamelCase'
+  - key:   readability-identifier-naming.ParameterCase
+    value: 'camelBack'
+  - key:   readability-identifier-naming.LocalVariableCase
+    value: 'camelBack'
 ...
diff --git a/Help/command/list.rst b/Help/command/list.rst
index a84e2cb..1280c40 100644
--- a/Help/command/list.rst
+++ b/Help/command/list.rst
@@ -399,6 +399,7 @@
 
 .. signature::
   list(SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])
+  list(SORT <list> COMPARATOR <function> [CASE <case>] [ORDER <order>])
 
   Sorts the list in-place alphabetically.
 
@@ -447,3 +448,47 @@
 
     ``DESCENDING``
       Sorts the list in descending order.
+
+  Instead of the built-in ``COMPARE`` methods, a user-defined comparison
+  function may be used with the ``COMPARATOR`` keyword.
+
+  .. versionadded:: 4.4
+
+  ``COMPARATOR`` is mutually exclusive with ``COMPARE``.  The ``CASE``
+  and ``ORDER`` options may still be used alongside ``COMPARATOR``:
+  the ``CASE`` filter is applied to both values before they are passed to
+  the callable, and ``ORDER DESCENDING`` reverses the comparison by
+  swapping the two arguments.
+
+  The callable must accept exactly three parameters: two input values and
+  the name of an output variable.  It must set the output variable to a
+  boolean value (``TRUE`` if the first value should come before the second,
+  ``FALSE`` otherwise) in the calling scope.
+  If the callable does not set the output variable, it is an error.
+
+  The comparator must define a
+  `strict weak ordering <https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings>`_.
+  In particular:
+
+  * It must return ``FALSE`` when comparing an element to itself
+    (irreflexivity).
+  * If it returns ``TRUE`` for ``(a, b)``, it must return ``FALSE``
+    for ``(b, a)`` (asymmetry).
+
+  An error is raised if the comparator violates these requirements.
+
+  Example:
+
+  .. code-block:: cmake
+
+    function(version_less a b result)
+      if("${a}" VERSION_LESS "${b}")
+        set(${result} TRUE PARENT_SCOPE)
+      else()
+        set(${result} FALSE PARENT_SCOPE)
+      endif()
+    endfunction()
+
+    set(versions 3.1 1.2 2.0 1.10)
+    list(SORT versions COMPARATOR version_less)
+    # versions is now: 1.2;1.10;2.0;3.1
diff --git a/Help/release/dev/list-SORT-COMPARATOR.rst b/Help/release/dev/list-SORT-COMPARATOR.rst
new file mode 100644
index 0000000..61a658d
--- /dev/null
+++ b/Help/release/dev/list-SORT-COMPARATOR.rst
@@ -0,0 +1,6 @@
+list-SORT-COMPARATOR
+--------------------
+
+* The :command:`list(SORT)` command gained a new ``COMPARATOR`` keyword
+  that invokes a user-defined :command:`function` to compare elements, complementing
+  the built-in ``COMPARE`` methods.
diff --git a/Modules/CMakeASM_NASMInformation.cmake b/Modules/CMakeASM_NASMInformation.cmake
index 54e5153..c60aef0 100644
--- a/Modules/CMakeASM_NASMInformation.cmake
+++ b/Modules/CMakeASM_NASMInformation.cmake
@@ -8,30 +8,34 @@
 
 if(NOT CMAKE_ASM_NASM_OBJECT_FORMAT)
   if(WIN32)
-    if(DEFINED CMAKE_C_SIZEOF_DATA_PTR AND CMAKE_C_SIZEOF_DATA_PTR EQUAL 8)
+    if(CMAKE_C_SIZEOF_DATA_PTR EQUAL 8 OR CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8)
       set(CMAKE_ASM_NASM_OBJECT_FORMAT win64)
-    elseif(DEFINED CMAKE_CXX_SIZEOF_DATA_PTR AND CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8)
-      set(CMAKE_ASM_NASM_OBJECT_FORMAT win64)
+    elseif(CMAKE_C_SIZEOF_DATA_PTR EQUAL 4 OR CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 4)
+      set(CMAKE_ASM_NASM_OBJECT_FORMAT win32)
     elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
       set(CMAKE_ASM_NASM_OBJECT_FORMAT win64)
     else()
       set(CMAKE_ASM_NASM_OBJECT_FORMAT win32)
     endif()
   elseif(APPLE)
-    if(DEFINED CMAKE_C_SIZEOF_DATA_PTR AND CMAKE_C_SIZEOF_DATA_PTR EQUAL 8)
+    if(CMAKE_C_SIZEOF_DATA_PTR EQUAL 8 OR CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8)
       set(CMAKE_ASM_NASM_OBJECT_FORMAT macho64)
-    elseif(DEFINED CMAKE_CXX_SIZEOF_DATA_PTR AND CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8)
-      set(CMAKE_ASM_NASM_OBJECT_FORMAT macho64)
+    elseif(CMAKE_C_SIZEOF_DATA_PTR EQUAL 4 OR CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 4)
+      set(CMAKE_ASM_NASM_OBJECT_FORMAT macho32)
     elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
       set(CMAKE_ASM_NASM_OBJECT_FORMAT macho64)
     else()
       set(CMAKE_ASM_NASM_OBJECT_FORMAT macho)
     endif()
   else()
-    if(DEFINED CMAKE_C_SIZEOF_DATA_PTR AND CMAKE_C_SIZEOF_DATA_PTR EQUAL 8)
+    if(CMAKE_C_SIZEOF_DATA_PTR EQUAL 8 OR CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8)
       set(CMAKE_ASM_NASM_OBJECT_FORMAT elf64)
-    elseif(DEFINED CMAKE_CXX_SIZEOF_DATA_PTR AND CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8)
-      set(CMAKE_ASM_NASM_OBJECT_FORMAT elf64)
+    elseif(CMAKE_C_SIZEOF_DATA_PTR EQUAL 4 OR CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 4)
+      if(";${CMAKE_C_COMPILER_ARCHITECTURE_ID};${CMAKE_CXX_COMPILER_ARCHITECTURE_ID};" MATCHES ";x86_64;")
+        set(CMAKE_ASM_NASM_OBJECT_FORMAT elfx32)
+      else()
+        set(CMAKE_ASM_NASM_OBJECT_FORMAT elf32)
+      endif()
     elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
       set(CMAKE_ASM_NASM_OBJECT_FORMAT elf64)
     else()
@@ -41,7 +45,7 @@
 endif()
 
 if(NOT CMAKE_ASM_NASM_COMPILE_OBJECT)
-  set(CMAKE_ASM_NASM_COMPILE_OBJECT "<CMAKE_ASM_NASM_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -f ${CMAKE_ASM_NASM_OBJECT_FORMAT} -o <OBJECT> <SOURCE>")
+  set(CMAKE_ASM_NASM_COMPILE_OBJECT "<CMAKE_ASM_NASM_COMPILER> <DEFINES> <INCLUDES> -f ${CMAKE_ASM_NASM_OBJECT_FORMAT} <FLAGS> -o <OBJECT> <SOURCE>")
 endif()
 
 if(NOT CMAKE_ASM_NASM_LINK_EXECUTABLE)
diff --git a/Modules/CMakeDetermineRustCompiler.cmake b/Modules/CMakeDetermineRustCompiler.cmake
index 43441b0..971354f 100644
--- a/Modules/CMakeDetermineRustCompiler.cmake
+++ b/Modules/CMakeDetermineRustCompiler.cmake
@@ -30,9 +30,10 @@
     COMMAND ${RUSTUP_PATH} which rustc
     OUTPUT_VARIABLE REAL_RUSTC
     OUTPUT_STRIP_TRAILING_WHITESPACE
-  )
+    RESULT_VARIABLE _rust_result
+    )
 
-  if("${REAL_RUSTC}" STREQUAL "")
+  if(NOT _rust_result EQUAL 0 OR "${REAL_RUSTC}" STREQUAL "")
     message(FATAL_ERROR "Failed to find path to real rustc")
   endif()
 
diff --git a/Modules/CMakeDetermineSystem.cmake b/Modules/CMakeDetermineSystem.cmake
index dc26258..c70fea1 100644
--- a/Modules/CMakeDetermineSystem.cmake
+++ b/Modules/CMakeDetermineSystem.cmake
@@ -15,19 +15,24 @@
       execute_process(COMMAND ${CMAKE_UNAME} -v
         OUTPUT_VARIABLE _CMAKE_HOST_SYSTEM_MAJOR_VERSION
         OUTPUT_STRIP_TRAILING_WHITESPACE
-        ERROR_QUIET)
+        ERROR_QUIET
+        RESULT_VARIABLE _uname_result)
       execute_process(COMMAND ${CMAKE_UNAME} -r
         OUTPUT_VARIABLE _CMAKE_HOST_SYSTEM_MINOR_VERSION
         OUTPUT_STRIP_TRAILING_WHITESPACE
-        ERROR_QUIET)
-      set(CMAKE_HOST_SYSTEM_VERSION "${_CMAKE_HOST_SYSTEM_MAJOR_VERSION}.${_CMAKE_HOST_SYSTEM_MINOR_VERSION}")
+        ERROR_QUIET
+        RESULT_VARIABLE _uname_result2)
+      if(_uname_result EQUAL 0 AND _uname_result2 EQUAL 0)
+        set(CMAKE_HOST_SYSTEM_VERSION "${_CMAKE_HOST_SYSTEM_MAJOR_VERSION}.${_CMAKE_HOST_SYSTEM_MINOR_VERSION}")
+      endif()
       unset(_CMAKE_HOST_SYSTEM_MAJOR_VERSION)
       unset(_CMAKE_HOST_SYSTEM_MINOR_VERSION)
     elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Android")
       execute_process(COMMAND getprop ro.build.version.sdk
         OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION
         OUTPUT_STRIP_TRAILING_WHITESPACE
-        ERROR_QUIET)
+        ERROR_QUIET
+        RESULT_VARIABLE _uname_result)
 
       if(NOT DEFINED CMAKE_SYSTEM_VERSION)
         set(_ANDROID_API_LEVEL_H $ENV{PREFIX}/include/android/api-level.h)
@@ -49,7 +54,8 @@
       execute_process(COMMAND ${CMAKE_UNAME} -r
         OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION
         OUTPUT_STRIP_TRAILING_WHITESPACE
-        ERROR_QUIET)
+        ERROR_QUIET
+        RESULT_VARIABLE _uname_result)
     endif()
     if(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux|CYGWIN.*|MSYS.*|^GNU$|Android")
       execute_process(COMMAND ${CMAKE_UNAME} -m
diff --git a/Modules/CMakeTestFortranCompiler.cmake b/Modules/CMakeTestFortranCompiler.cmake
index 04c790b..2a22ff2 100644
--- a/Modules/CMakeTestFortranCompiler.cmake
+++ b/Modules/CMakeTestFortranCompiler.cmake
@@ -65,7 +65,7 @@
 # Test for Fortran 90 support by using an f90-specific construct.
 if(NOT DEFINED CMAKE_Fortran_COMPILER_SUPPORTS_F90)
   message(CHECK_START "Checking whether ${CMAKE_Fortran_COMPILER} supports Fortran 90")
-  set(__TestCompiler_testFortranCompilerSource "
+  set(__TestCompiler_testFortranCompilerF90Source "
     PROGRAM TESTFortran90
     integer stop ; stop = 1 ; do while ( stop .eq. 0 ) ; end do
     END PROGRAM TESTFortran90
diff --git a/Modules/Compiler/ARMClang.cmake b/Modules/Compiler/ARMClang.cmake
index 2f0ee91..ae78b3b 100644
--- a/Modules/Compiler/ARMClang.cmake
+++ b/Modules/Compiler/ARMClang.cmake
@@ -25,9 +25,14 @@
 function(__armclang_set_processor_list lang out_var)
   execute_process(COMMAND "${CMAKE_${lang}_COMPILER}" --target=${CMAKE_${lang}_COMPILER_TARGET} -mcpu=list
     OUTPUT_VARIABLE processor_list
-    ERROR_VARIABLE processor_list)
-  string(REGEX MATCHALL "-mcpu=([^ \n]*)" processor_list "${processor_list}")
-  string(REGEX REPLACE "-mcpu=" "" processor_list "${processor_list}")
+    ERROR_VARIABLE processor_list
+    RESULT_VARIABLE _res
+    )
+  set(processor_list)
+  if(_res EQUAL 0)
+    string(REGEX MATCHALL "-mcpu=([^ \n]*)" processor_list "${processor_list}")
+    string(REGEX REPLACE "-mcpu=" "" processor_list "${processor_list}")
+  endif()
   set(${out_var} "${processor_list}" PARENT_SCOPE)
 endfunction()
 
@@ -45,9 +50,14 @@
 function(__armclang_set_arch_list lang out_var)
   execute_process(COMMAND "${CMAKE_${lang}_COMPILER}" --target=${CMAKE_${lang}_COMPILER_TARGET} -march=list
     OUTPUT_VARIABLE arch_list
-    ERROR_VARIABLE arch_list)
-  string(REGEX MATCHALL "-march=([^ \n]*)" arch_list "${arch_list}")
-  string(REGEX REPLACE "-march=" "" arch_list "${arch_list}")
+    ERROR_VARIABLE arch_list
+    RESULT_VARIABLE _res
+    )
+  set(arch_list)
+  if(_res EQUAL 0)
+    string(REGEX MATCHALL "-march=([^ \n]*)" arch_list "${arch_list}")
+    string(REGEX REPLACE "-march=" "" arch_list "${arch_list}")
+  endif()
   set(${out_var} "${arch_list}" PARENT_SCOPE)
 endfunction()
 
@@ -61,9 +71,14 @@
 
   execute_process(COMMAND "${CMAKE_LINKER}" ${__linker_wrapper_flags} --cpu=list
     OUTPUT_VARIABLE cpu_list
-    ERROR_VARIABLE cpu_list)
-  string(REGEX MATCHALL "--cpu=([^ \n]*)" cpu_list "${cpu_list}")
-  string(REGEX REPLACE "--cpu=" "" cpu_list "${cpu_list}")
+    ERROR_VARIABLE cpu_list
+    RESULT_VARIABLE _res
+    )
+  set(cpu_list)
+  if(_res EQUAL 0)
+    string(REGEX MATCHALL "--cpu=([^ \n]*)" cpu_list "${cpu_list}")
+    string(REGEX REPLACE "--cpu=" "" cpu_list "${cpu_list}")
+  endif()
   set(${out_var} "${cpu_list}" PARENT_SCOPE)
 endfunction()
 
diff --git a/Modules/Compiler/NAG-Fortran.cmake b/Modules/Compiler/NAG-Fortran.cmake
index 50b2991..9d12677 100644
--- a/Modules/Compiler/NAG-Fortran.cmake
+++ b/Modules/Compiler/NAG-Fortran.cmake
@@ -6,10 +6,11 @@
     COMMAND ${CMAKE_Fortran_COMPILER} dummy.o -dryrun
     OUTPUT_VARIABLE _dryrun
     ERROR_VARIABLE _dryrun
+    RESULT_VARIABLE _dryrun_result
     )
   # Match an object file.
   string(REGEX MATCH "/[^ ]*/[^ /][^ /]*\\.o" _nag_obj "${_dryrun}")
-  if(_nag_obj)
+  if(_nag_obj AND _dryrun_result EQUAL 0)
     # Parse object directory and convert to a regex.
     string(REGEX REPLACE "/[^/]*$" "" _nag_dir "${_nag_obj}")
     string(REGEX REPLACE "([][+.*()^])" "\\\\\\1" _nag_regex "${_nag_dir}")
@@ -44,7 +45,13 @@
 set(CMAKE_Fortran_FORMAT_FREE_FLAG "-free")
 set(CMAKE_Fortran_COMPILE_OPTIONS_PIC "-PIC")
 set(CMAKE_Fortran_COMPILE_OPTIONS_PIE "-PIC")
-set(CMAKE_Fortran_RESPONSE_FILE_LINK_FLAG "-Wl,@")
+if(CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL 6.2)
+  set(CMAKE_Fortran_RESPONSE_FILE_LINK_FLAG "-xldarg @")
+  set(CMAKE_Fortran_RESPONSE_FILE_ARCHIVE_FLAG "@")
+  set(CMAKE_Fortran_USE_RESPONSE_FILE_FOR_LIBRARIES 0)
+else()
+  set(CMAKE_Fortran_RESPONSE_FILE_LINK_FLAG "@")
+endif()
 set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-fpp")
 
 set(CMAKE_Fortran_LINK_MODE DRIVER)
diff --git a/Modules/FindGit.cmake b/Modules/FindGit.cmake
index 978746b..878e1a5 100644
--- a/Modules/FindGit.cmake
+++ b/Modules/FindGit.cmake
@@ -162,8 +162,9 @@
     execute_process(COMMAND ${GIT_EXECUTABLE} --version
                     OUTPUT_VARIABLE git_version
                     ERROR_QUIET
-                    OUTPUT_STRIP_TRAILING_WHITESPACE)
-    if (git_version MATCHES "^git version [0-9]")
+                    OUTPUT_STRIP_TRAILING_WHITESPACE
+                    RESULT_VARIABLE _git_res)
+    if (_git_res EQUAL 0 AND git_version MATCHES "^git version [0-9]")
       string(REPLACE "git version " "" Git_VERSION "${git_version}")
       set(GIT_VERSION_STRING "${Git_VERSION}")
       set_property(GLOBAL PROPERTY _CMAKE_FindGit_GIT_EXECUTABLE_VERSION
diff --git a/Modules/Internal/CMakeDetermineLinkerId.cmake b/Modules/Internal/CMakeDetermineLinkerId.cmake
index 1664921..7e2e433 100644
--- a/Modules/Internal/CMakeDetermineLinkerId.cmake
+++ b/Modules/Internal/CMakeDetermineLinkerId.cmake
@@ -31,7 +31,10 @@
                     OUTPUT_STRIP_TRAILING_WHITESPACE
                     ERROR_STRIP_TRAILING_WHITESPACE
                     COMMAND_ERROR_IS_FATAL NONE
+                    RESULT_VARIABLE _res
     )
+    # We capture "_res" but don't check it because it's expected that
+    # linkers will exit with non-zero on flags they do not have.
 
     string(JOIN "\" \"" flags_string ${flags})
     string(REGEX REPLACE "\n\n.*" "" linker_desc_head "${linker_desc}")
diff --git a/Modules/Linker/GNU.cmake b/Modules/Linker/GNU.cmake
index b56608e..de8f8e2 100644
--- a/Modules/Linker/GNU.cmake
+++ b/Modules/Linker/GNU.cmake
@@ -24,11 +24,13 @@
       # Ninja 1.10 or upper is required
       execute_process(COMMAND "${CMAKE_MAKE_PROGRAM}" --version
         OUTPUT_VARIABLE _ninja_version
-        ERROR_VARIABLE _ninja_version)
+        ERROR_VARIABLE _ninja_version
+        RESULT_VARIABLE _res
+        )
       if (_ninja_version MATCHES "[0-9]+(\\.[0-9]+)*")
         set (_ninja_version "${CMAKE_MATCH_0}")
       endif()
-      if (_ninja_version VERSION_LESS "1.10")
+      if (NOT _res EQUAL 0 OR _ninja_version VERSION_LESS "1.10")
         set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED FALSE)
       endif()
     endif()
@@ -38,8 +40,9 @@
       if (CMAKE_${lang}_COMPILER_LINKER AND CMAKE_${lang}_COMPILER_LINKER_ID MATCHES "GNU|LLD|MOLD")
         execute_process(COMMAND "${CMAKE_${lang}_COMPILER_LINKER}" --help
                         OUTPUT_VARIABLE _linker_capabilities
-                        ERROR_VARIABLE _linker_capabilities)
-        if(_linker_capabilities MATCHES "--dependency-file")
+                        ERROR_VARIABLE _linker_capabilities
+                        RESULT_VARIABLE _res)
+        if(_res EQUAL 0 AND _linker_capabilities MATCHES "--dependency-file")
           set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED TRUE)
         else()
           set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED FALSE)
diff --git a/Modules/Platform/Android/Determine-Compiler-Standalone.cmake b/Modules/Platform/Android/Determine-Compiler-Standalone.cmake
index 2da1ad3..30a0125 100644
--- a/Modules/Platform/Android/Determine-Compiler-Standalone.cmake
+++ b/Modules/Platform/Android/Determine-Compiler-Standalone.cmake
@@ -30,8 +30,9 @@
   OUTPUT_VARIABLE _gcc_version
   ERROR_VARIABLE _gcc_error
   OUTPUT_STRIP_TRAILING_WHITESPACE
+  RESULT_VARIABLE _gcc_result
   )
-if(_gcc_version MATCHES "^([0-9]+\\.[0-9]+)")
+if(_gcc_result EQUAL 0 AND _gcc_version MATCHES "^([0-9]+\\.[0-9]+)")
   set(_ANDROID_TOOL_C_TOOLCHAIN_VERSION "${CMAKE_MATCH_1}")
 else()
   message(FATAL_ERROR
diff --git a/Modules/Platform/Apple-GNU.cmake b/Modules/Platform/Apple-GNU.cmake
index 69ccb40..1852c6e 100644
--- a/Modules/Platform/Apple-GNU.cmake
+++ b/Modules/Platform/Apple-GNU.cmake
@@ -31,8 +31,9 @@
       COMMAND ${CMAKE_${lang}_COMPILER} "-v" "--help"
       OUTPUT_VARIABLE _gcc_help
       ERROR_VARIABLE _gcc_help
+      RESULT_VARIABLE _gcc_result
       )
-    if("${_gcc_help}" MATCHES "isysroot")
+    if(_gcc_result EQUAL 0 AND "${_gcc_help}" MATCHES "isysroot")
       message(CHECK_PASS "yes")
       set(CMAKE_${lang}_SYSROOT_FLAG "-isysroot")
     else()
@@ -51,8 +52,9 @@
       COMMAND ${CMAKE_${lang}_COMPILER} "-v" "--help"
       OUTPUT_VARIABLE _gcc_help
       ERROR_VARIABLE _gcc_help
+      RESULT_VARIABLE _gcc_result
       )
-    if("${_gcc_help}" MATCHES "macosx-version-min")
+    if(_gcc_result EQUAL 0 AND "${_gcc_help}" MATCHES "macosx-version-min")
       message(CHECK_PASS "yes")
       set(CMAKE_${lang}_OSX_DEPLOYMENT_TARGET_FLAG "-mmacosx-version-min=")
     else()
diff --git a/Modules/Platform/Darwin-Initialize.cmake b/Modules/Platform/Darwin-Initialize.cmake
index fe42de1..c9c88b3 100644
--- a/Modules/Platform/Darwin-Initialize.cmake
+++ b/Modules/Platform/Darwin-Initialize.cmake
@@ -292,10 +292,11 @@
 if(NOT CMAKE_CROSSCOMPILING)
   execute_process(COMMAND sw_vers -productVersion
     OUTPUT_VARIABLE _CMAKE_HOST_OSX_VERSION
-    OUTPUT_STRIP_TRAILING_WHITESPACE)
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    RESULT_VARIABLE _result)
 endif()
 
-if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET)
+if(_result EQUAL 0 AND CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET)
   set(_CMAKE_OSX_DEPLOYMENT_TARGET_DEFAULT "$ENV{MACOSX_DEPLOYMENT_TARGET}")
 
   # Xcode chooses a default macOS deployment target based on the macOS SDK
diff --git a/Modules/Platform/Linker/BSD-Linker-Initialize.cmake b/Modules/Platform/Linker/BSD-Linker-Initialize.cmake
index b28dc06..2b5d598 100644
--- a/Modules/Platform/Linker/BSD-Linker-Initialize.cmake
+++ b/Modules/Platform/Linker/BSD-Linker-Initialize.cmake
@@ -7,7 +7,7 @@
                     RESULT_VARIABLE result
                     OUTPUT_VARIABLE output
                     ERROR_VARIABLE output)
-    if(result OR NOT output MATCHES "LLD")
+    if(NOT result EQUAL 0 OR NOT output MATCHES "LLD")
       # assume GNU as default linker
       set(_CMAKE_SYSTEM_LINKER_TYPE GNU CACHE INTERNAL "System linker type")
     else()
diff --git a/Modules/Platform/Linker/GNU.cmake b/Modules/Platform/Linker/GNU.cmake
index 3e665fa..f6b2137 100644
--- a/Modules/Platform/Linker/GNU.cmake
+++ b/Modules/Platform/Linker/GNU.cmake
@@ -23,8 +23,8 @@
                     OUTPUT_VARIABLE __linker_log
                     ERROR_VARIABLE __linker_log
                     COMMAND_ERROR_IS_FATAL NONE
-    )
-    if(__linker_log MATCHES "--push-state" OR __linker_log MATCHES "--pop-state")
+                    RESULT_VARIABLE __linker_result)
+    if(__linker_result EQUAL 0 AND (__linker_log MATCHES "--push-state" OR __linker_log MATCHES "--pop-state"))
       set(CMAKE_${__lang}LINKER_PUSHPOP_STATE_SUPPORTED FALSE)
     else()
       set(CMAKE_${__lang}LINKER_PUSHPOP_STATE_SUPPORTED TRUE)
diff --git a/Modules/Platform/Linux-SunPro-CXX.cmake b/Modules/Platform/Linux-SunPro-CXX.cmake
index a07f1ec..fe25deb 100644
--- a/Modules/Platform/Linux-SunPro-CXX.cmake
+++ b/Modules/Platform/Linux-SunPro-CXX.cmake
@@ -1,7 +1,10 @@
 # Sun C++ 5.9 does not support -Wl, but Sun C++ 5.11 does not work without it.
 # Query the compiler flags to detect whether to use -Wl.
-execute_process(COMMAND ${CMAKE_CXX_COMPILER} -flags OUTPUT_VARIABLE _cxx_flags ERROR_VARIABLE _cxx_error)
-if("${_cxx_flags}" MATCHES "\n-W[^\n]*component")
+execute_process(COMMAND ${CMAKE_CXX_COMPILER} -flags
+OUTPUT_VARIABLE _cxx_flags
+ERROR_VARIABLE _cxx_error
+RESULT_VARIABLE _cxx_result)
+if(_cxx_result EQUAL 0 AND "${_cxx_flags}" MATCHES "\n-W[^\n]*component")
   set(CMAKE_SHARED_LIBRARY_RPATH_LINK_CXX_FLAG "-Wl,-rpath-link,")
 else()
   set(CMAKE_SHARED_LIBRARY_RPATH_LINK_CXX_FLAG "-rpath-link ")
diff --git a/Modules/Platform/OpenBSD.cmake b/Modules/Platform/OpenBSD.cmake
index 97e2a6a..71bb99e 100644
--- a/Modules/Platform/OpenBSD.cmake
+++ b/Modules/Platform/OpenBSD.cmake
@@ -7,12 +7,15 @@
 if(NOT CMAKE_PLATFORM_RUNTIME_PATH)
   execute_process(COMMAND /sbin/ldconfig -r
                   OUTPUT_VARIABLE LDCONFIG_HINTS
-                  ERROR_QUIET)
-  string(REGEX REPLACE ".*search\\ directories:\\ ([^\n]*).*" "\\1"
-         LDCONFIG_HINTS "${LDCONFIG_HINTS}")
-  string(REPLACE ":" ";"
-         CMAKE_PLATFORM_RUNTIME_PATH
-         "${LDCONFIG_HINTS}")
+                  ERROR_QUIET
+                  RESULT_VARIABLE _res)
+  if(_res EQUAL 0)
+    string(REGEX REPLACE ".*search\\ directories:\\ ([^\n]*).*" "\\1"
+           LDCONFIG_HINTS "${LDCONFIG_HINTS}")
+    string(REPLACE ":" ";"
+           CMAKE_PLATFORM_RUNTIME_PATH
+           "${LDCONFIG_HINTS}")
+  endif()
 endif()
 
 # OpenBSD requires -z origin to enable $ORIGIN expansion in RPATH.
diff --git a/Modules/Platform/Windows-GNU.cmake b/Modules/Platform/Windows-GNU.cmake
index 962b57a..c50b13c 100644
--- a/Modules/Platform/Windows-GNU.cmake
+++ b/Modules/Platform/Windows-GNU.cmake
@@ -58,8 +58,8 @@
 
 # Check if GNU ld is too old to support @FILE syntax.
 set(__WINDOWS_GNU_LD_RESPONSE 1)
-execute_process(COMMAND ld -v OUTPUT_VARIABLE _help ERROR_VARIABLE _help)
-if("${_help}" MATCHES "GNU ld .* 2\\.1[1-6]")
+execute_process(COMMAND ld -v OUTPUT_VARIABLE _help ERROR_VARIABLE _help RESULT_VARIABLE _res)
+if(_res EQUAL 0 AND "${_help}" MATCHES "GNU ld .* 2\\.1[1-6]")
   set(__WINDOWS_GNU_LD_RESPONSE 0)
 endif()
 
@@ -107,8 +107,8 @@
   set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_INCLUDES 1)
 
   # We prefer "@" for response files but it is not supported by gcc 3.
-  execute_process(COMMAND ${CMAKE_${lang}_COMPILER} --version OUTPUT_VARIABLE _ver ERROR_VARIABLE _ver)
-  if("${_ver}" MATCHES "\\(GCC\\) 3\\.")
+  execute_process(COMMAND ${CMAKE_${lang}_COMPILER} --version OUTPUT_VARIABLE _ver ERROR_VARIABLE _ver RESULT_VARIABLE _res)
+  if(_res EQUAL 0 AND "${_ver}" MATCHES "\\(GCC\\) 3\\.")
     if("${lang}" STREQUAL "Fortran")
       # The GNU Fortran compiler reports an error:
       #   no input files; unwilling to write output files
diff --git a/Modules/Platform/Windows-Intel.cmake b/Modules/Platform/Windows-Intel.cmake
index 12598a0..46a934d 100644
--- a/Modules/Platform/Windows-Intel.cmake
+++ b/Modules/Platform/Windows-Intel.cmake
@@ -16,7 +16,7 @@
     RESULT_VARIABLE _CMAKE_NINJA_RESULT
     OUTPUT_VARIABLE _CMAKE_NINJA_VERSION
     ERROR_VARIABLE _CMAKE_NINJA_VERSION)
-  if (NOT _CMAKE_NINJA_RESULT AND _CMAKE_NINJA_VERSION MATCHES "[0-9]+(\\.[0-9]+)*")
+  if (_CMAKE_NINJA_RESULT EQUAL 0 AND _CMAKE_NINJA_VERSION MATCHES "[0-9]+(\\.[0-9]+)*")
     set (_CMAKE_NINJA_VERSION "${CMAKE_MATCH_0}")
   endif()
   unset(_CMAKE_NINJA_RESULT)
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index d931fa3..956232c 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 4)
 set(CMake_VERSION_MINOR 3)
-set(CMake_VERSION_PATCH 20260506)
+set(CMake_VERSION_PATCH 20260508)
 #set(CMake_VERSION_RC 0)
 set(CMake_VERSION_IS_DIRTY 0)
 
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx
index 7f41fcf..62e3d80 100644
--- a/Source/cmComputeLinkInformation.cxx
+++ b/Source/cmComputeLinkInformation.cxx
@@ -1084,15 +1084,35 @@
   }
 }
 
+namespace {
+std::string const gcc_s = "gcc_s";
+std::string const gcc_s_asneeded = "gcc_s_asneeded";
+}
+
 void cmComputeLinkInformation::AddImplicitLinkInfo(std::string const& lang)
 {
+  auto const impliedByLinkerLanguage = [this](std::string const& lib) -> bool {
+    if (cm::contains(this->ImplicitLinkLibs, lib)) {
+      return true;
+    }
+    // As of GCC 16, `gcc` implies `gcc_s_asneeded` but `g++` implies `gcc_s`.
+    // Accept them interchangeably when linking mixed C and C++ binaries.
+    if ((lib == gcc_s_asneeded &&
+         cm::contains(this->ImplicitLinkLibs, gcc_s)) ||
+        (lib == gcc_s &&
+         cm::contains(this->ImplicitLinkLibs, gcc_s_asneeded))) {
+      return true;
+    }
+    return false;
+  };
+
   // Add libraries for this language that are not implied by the
   // linker language.
   std::string libVar = cmStrCat("CMAKE_", lang, "_IMPLICIT_LINK_LIBRARIES");
   if (cmValue libs = this->Makefile->GetDefinition(libVar)) {
     cmList libsList{ *libs };
-    for (auto const& i : libsList) {
-      if (!cm::contains(this->ImplicitLinkLibs, i)) {
+    for (std::string const& i : libsList) {
+      if (!impliedByLinkerLanguage(i)) {
         this->AddItem({ i });
       }
     }
diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx
index 8137d69..5a3c72d 100644
--- a/Source/cmInstallCommand.cxx
+++ b/Source/cmInstallCommand.cxx
@@ -306,10 +306,10 @@
 
   std::string component = helper.DefaultComponentName;
   int componentCount = 0;
-  bool doing_script = false;
-  bool doing_code = false;
-  bool exclude_from_all = false;
-  bool all_components = false;
+  bool doingScript = false;
+  bool doingCode = false;
+  bool excludeFromAll = false;
+  bool allComponents = false;
 
   // Scan the args once for COMPONENT. Only allow one.
   //
@@ -320,9 +320,9 @@
       component = args[i];
     }
     if (args[i] == "EXCLUDE_FROM_ALL") {
-      exclude_from_all = true;
+      excludeFromAll = true;
     } else if (args[i] == "ALL_COMPONENTS") {
-      all_components = true;
+      allComponents = true;
     }
   }
 
@@ -333,7 +333,7 @@
     return false;
   }
 
-  if (all_components && componentCount == 1) {
+  if (allComponents && componentCount == 1) {
     status.SetError("ALL_COMPONENTS and COMPONENT are mutually exclusive");
     return false;
   }
@@ -343,16 +343,16 @@
   //
   for (std::string const& arg : args) {
     if (arg == "SCRIPT") {
-      doing_script = true;
-      doing_code = false;
+      doingScript = true;
+      doingCode = false;
     } else if (arg == "CODE") {
-      doing_script = false;
-      doing_code = true;
+      doingScript = false;
+      doingCode = true;
     } else if (arg == "COMPONENT") {
-      doing_script = false;
-      doing_code = false;
-    } else if (doing_script) {
-      doing_script = false;
+      doingScript = false;
+      doingCode = false;
+    } else if (doingScript) {
+      doingScript = false;
       std::string script = arg;
       if (!cmHasLiteralPrefix(script, "$<INSTALL_PREFIX>")) {
         if (!cmSystemTools::FileIsFullPath(script)) {
@@ -366,23 +366,23 @@
       }
       helper.Makefile->AddInstallGenerator(
         cm::make_unique<cmInstallScriptGenerator>(
-          script, false, component, exclude_from_all, all_components,
+          script, false, component, excludeFromAll, allComponents,
           helper.Makefile->GetBacktrace()));
-    } else if (doing_code) {
-      doing_code = false;
+    } else if (doingCode) {
+      doingCode = false;
       std::string const& code = arg;
       helper.Makefile->AddInstallGenerator(
         cm::make_unique<cmInstallScriptGenerator>(
-          code, true, component, exclude_from_all, all_components,
+          code, true, component, excludeFromAll, allComponents,
           helper.Makefile->GetBacktrace()));
     }
   }
 
-  if (doing_script) {
+  if (doingScript) {
     status.SetError("given no value for SCRIPT argument.");
     return false;
   }
-  if (doing_code) {
+  if (doingCode) {
     status.SetError("given no value for CODE argument.");
     return false;
   }
@@ -699,11 +699,11 @@
     cmTarget* target = helper.Makefile->FindLocalNonAliasTarget(tgt);
     if (!target) {
       // If no local target has been found, find it in the global scope.
-      cmTarget* const global_target =
+      cmTarget* const globalTarget =
         helper.Makefile->GetGlobalGenerator()->FindTarget(
           tgt, { cmStateEnums::TargetDomain::NATIVE });
-      if (global_target && !global_target->IsImported()) {
-        target = global_target;
+      if (globalTarget && !globalTarget->IsImported()) {
+        target = globalTarget;
       }
     }
     if (target) {
@@ -1364,11 +1364,11 @@
     cmTarget* target = helper.Makefile->FindTargetToUse(tgt);
     if (!target || !target->IsImported()) {
       // If no local target has been found, find it in the global scope.
-      cmTarget* const global_target =
+      cmTarget* const globalTarget =
         helper.Makefile->GetGlobalGenerator()->FindTarget(
           tgt, { cmStateEnums::TargetDomain::NATIVE });
-      if (global_target && global_target->IsImported()) {
-        target = global_target;
+      if (globalTarget && globalTarget->IsImported()) {
+        target = globalTarget;
       }
     }
     if (target) {
@@ -1634,21 +1634,21 @@
     DoingType
   };
   Doing doing = DoingDirs;
-  bool in_match_mode = false;
+  bool inMatchMode = false;
   bool optional = false;
-  bool exclude_from_all = false;
-  bool message_never = false;
+  bool excludeFromAll = false;
+  bool messageNever = false;
   std::vector<std::string> dirs;
   cm::optional<std::string> destination;
-  std::string permissions_file;
-  std::string permissions_dir;
+  std::string permissionsFile;
+  std::string permissionsDir;
   std::vector<std::string> configurations;
   std::string component = helper.DefaultComponentName;
-  std::string literal_args;
+  std::string literalArgs;
   std::string type;
   for (unsigned int i = 1; i < args.size(); ++i) {
     if (args[i] == "DESTINATION") {
-      if (in_match_mode) {
+      if (inMatchMode) {
         status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
                                  "\" after PATTERN or REGEX."));
         return false;
@@ -1657,7 +1657,7 @@
       // Switch to setting the destination property.
       doing = DoingDestination;
     } else if (args[i] == "TYPE") {
-      if (in_match_mode) {
+      if (inMatchMode) {
         status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
                                  "\" after PATTERN or REGEX."));
         return false;
@@ -1666,7 +1666,7 @@
       // Switch to setting the type.
       doing = DoingType;
     } else if (args[i] == "OPTIONAL") {
-      if (in_match_mode) {
+      if (inMatchMode) {
         status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
                                  "\" after PATTERN or REGEX."));
         return false;
@@ -1676,44 +1676,44 @@
       optional = true;
       doing = DoingNone;
     } else if (args[i] == "MESSAGE_NEVER") {
-      if (in_match_mode) {
+      if (inMatchMode) {
         status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
                                  "\" after PATTERN or REGEX."));
         return false;
       }
 
       // Mark the rule as quiet.
-      message_never = true;
+      messageNever = true;
       doing = DoingNone;
     } else if (args[i] == "PATTERN") {
       // Switch to a new pattern match rule.
       doing = DoingPattern;
-      in_match_mode = true;
+      inMatchMode = true;
     } else if (args[i] == "REGEX") {
       // Switch to a new regex match rule.
       doing = DoingRegex;
-      in_match_mode = true;
+      inMatchMode = true;
     } else if (args[i] == "EXCLUDE") {
       // Add this property to the current match rule.
-      if (!in_match_mode || doing == DoingPattern || doing == DoingRegex) {
+      if (!inMatchMode || doing == DoingPattern || doing == DoingRegex) {
         status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
                                  "\" before a PATTERN or REGEX is given."));
         return false;
       }
-      literal_args += " EXCLUDE";
+      literalArgs += " EXCLUDE";
       doing = DoingNone;
     } else if (args[i] == "PERMISSIONS") {
-      if (!in_match_mode) {
+      if (!inMatchMode) {
         status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
                                  "\" before a PATTERN or REGEX is given."));
         return false;
       }
 
       // Switch to setting the current match permissions property.
-      literal_args += " PERMISSIONS";
+      literalArgs += " PERMISSIONS";
       doing = DoingPermsMatch;
     } else if (args[i] == "FILE_PERMISSIONS") {
-      if (in_match_mode) {
+      if (inMatchMode) {
         status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
                                  "\" after PATTERN or REGEX."));
         return false;
@@ -1722,7 +1722,7 @@
       // Switch to setting the file permissions property.
       doing = DoingPermsFile;
     } else if (args[i] == "DIRECTORY_PERMISSIONS") {
-      if (in_match_mode) {
+      if (inMatchMode) {
         status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
                                  "\" after PATTERN or REGEX."));
         return false;
@@ -1731,27 +1731,27 @@
       // Switch to setting the directory permissions property.
       doing = DoingPermsDir;
     } else if (args[i] == "USE_SOURCE_PERMISSIONS") {
-      if (in_match_mode) {
+      if (inMatchMode) {
         status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
                                  "\" after PATTERN or REGEX."));
         return false;
       }
 
       // Add this option literally.
-      literal_args += " USE_SOURCE_PERMISSIONS";
+      literalArgs += " USE_SOURCE_PERMISSIONS";
       doing = DoingNone;
     } else if (args[i] == "FILES_MATCHING") {
-      if (in_match_mode) {
+      if (inMatchMode) {
         status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
                                  "\" after PATTERN or REGEX."));
         return false;
       }
 
       // Add this option literally.
-      literal_args += " FILES_MATCHING";
+      literalArgs += " FILES_MATCHING";
       doing = DoingNone;
     } else if (args[i] == "CONFIGURATIONS") {
-      if (in_match_mode) {
+      if (inMatchMode) {
         status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
                                  "\" after PATTERN or REGEX."));
         return false;
@@ -1760,7 +1760,7 @@
       // Switch to setting the configurations property.
       doing = DoingConfigurations;
     } else if (args[i] == "COMPONENT") {
-      if (in_match_mode) {
+      if (inMatchMode) {
         status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
                                  "\" after PATTERN or REGEX."));
         return false;
@@ -1769,12 +1769,12 @@
       // Switch to setting the component property.
       doing = DoingComponent;
     } else if (args[i] == "EXCLUDE_FROM_ALL") {
-      if (in_match_mode) {
+      if (inMatchMode) {
         status.SetError(cmStrCat(args[0], " does not allow \"", args[i],
                                  "\" after PATTERN or REGEX."));
         return false;
       }
-      exclude_from_all = true;
+      excludeFromAll = true;
       doing = DoingNone;
     } else if (doing == DoingDirs) {
       // If the given directory is not a full path, convert it to one by
@@ -1833,14 +1833,14 @@
       // leading slash and trailing end-of-string in the matched
       // string to make sure the pattern matches only whole file
       // names.
-      literal_args += " REGEX \"/";
+      literalArgs += " REGEX \"/";
       std::string regex = cmsys::Glob::PatternToRegex(args[i], false);
       cmSystemTools::ReplaceString(regex, "\\", "\\\\");
-      literal_args += regex;
-      literal_args += "$\"";
+      literalArgs += regex;
+      literalArgs += "$\"";
       doing = DoingNone;
     } else if (doing == DoingRegex) {
-      literal_args += " REGEX \"";
+      literalArgs += " REGEX \"";
 // Match rules are case-insensitive on some platforms.
 #if defined(_WIN32) || defined(__APPLE__)
       std::string regex = cmSystemTools::LowerCase(args[i]);
@@ -1848,8 +1848,8 @@
       std::string regex = args[i];
 #endif
       cmSystemTools::ReplaceString(regex, "\\", "\\\\");
-      literal_args += regex;
-      literal_args += "\"";
+      literalArgs += regex;
+      literalArgs += "\"";
       doing = DoingNone;
     } else if (doing == DoingComponent) {
       component = args[i];
@@ -1857,7 +1857,7 @@
     } else if (doing == DoingPermsFile) {
       // Check the requested permission.
       if (!cmInstallCommandArguments::CheckPermissions(args[i],
-                                                       permissions_file)) {
+                                                       permissionsFile)) {
         status.SetError(cmStrCat(args[0], " given invalid file permission \"",
                                  args[i], "\"."));
         return false;
@@ -1865,15 +1865,14 @@
     } else if (doing == DoingPermsDir) {
       // Check the requested permission.
       if (!cmInstallCommandArguments::CheckPermissions(args[i],
-                                                       permissions_dir)) {
+                                                       permissionsDir)) {
         status.SetError(cmStrCat(
           args[0], " given invalid directory permission \"", args[i], "\"."));
         return false;
       }
     } else if (doing == DoingPermsMatch) {
       // Check the requested permission.
-      if (!cmInstallCommandArguments::CheckPermissions(args[i],
-                                                       literal_args)) {
+      if (!cmInstallCommandArguments::CheckPermissions(args[i], literalArgs)) {
         status.SetError(
           cmStrCat(args[0], " given invalid permission \"", args[i], "\"."));
         return false;
@@ -1910,7 +1909,7 @@
   }
 
   cmInstallGenerator::MessageLevel message =
-    cmInstallGenerator::SelectMessageLevel(helper.Makefile, message_never);
+    cmInstallGenerator::SelectMessageLevel(helper.Makefile, messageNever);
 
   // Check for an absolute destination.
   if (cmGeneratorExpression::Find(*destination) == std::string::npos &&
@@ -1924,8 +1923,8 @@
   // Create the directory install generator.
   helper.Makefile->AddInstallGenerator(
     cm::make_unique<cmInstallDirectoryGenerator>(
-      dirs, *destination, permissions_file, permissions_dir, configurations,
-      component, message, exclude_from_all, literal_args, optional,
+      dirs, *destination, permissionsFile, permissionsDir, configurations,
+      component, message, excludeFromAll, literalArgs, optional,
       helper.Makefile->GetBacktrace()));
 
   // Tell the global generator about any installation component names
@@ -1945,12 +1944,12 @@
   cmInstallCommandArguments ica(helper.DefaultComponentName, *helper.Makefile);
 
   std::string exp;
-  std::string name_space;
+  std::string exportNamespace;
   bool exportOld = false;
   std::string filename;
 
   ica.Bind("EXPORT_ANDROID_MK"_s, exp);
-  ica.Bind("NAMESPACE"_s, name_space);
+  ica.Bind("NAMESPACE"_s, exportNamespace);
   ica.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, exportOld);
   ica.Bind("FILE"_s, filename);
 
@@ -2022,7 +2021,7 @@
     cm::make_unique<cmInstallAndroidMKExportGenerator>(
       &exportSet, ica.GetDestination(), ica.GetPermissions(),
       ica.GetConfigurations(), ica.GetComponent(), message,
-      ica.GetExcludeFromAll(), std::move(fname), std::move(name_space),
+      ica.GetExcludeFromAll(), std::move(fname), std::move(exportNamespace),
       helper.Makefile->GetBacktrace()));
 
   return true;
@@ -2188,17 +2187,17 @@
   cmInstallCommandArguments ica(helper.DefaultComponentName, *helper.Makefile);
 
   std::string exp;
-  std::string name_space;
+  std::string exportNamespace;
   bool exportOld = false;
   std::string filename;
-  std::string cxx_modules_directory;
+  std::string cxxModulesDirectory;
   bool exportPackageDependencies = false;
 
   ica.Bind("EXPORT"_s, exp);
-  ica.Bind("NAMESPACE"_s, name_space);
+  ica.Bind("NAMESPACE"_s, exportNamespace);
   ica.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, exportOld);
   ica.Bind("FILE"_s, filename);
-  ica.Bind("CXX_MODULES_DIRECTORY"_s, cxx_modules_directory);
+  ica.Bind("CXX_MODULES_DIRECTORY"_s, cxxModulesDirectory);
 
   if (cmExperimental::HasSupportEnabled(
         status.GetMakefile(),
@@ -2288,8 +2287,7 @@
         cm::optional<cm::string_view> const directive = MatchExport(pie, exp);
         if (directive) {
           if (!HandleMappedPackageInfo(exportSet, *directive, helper, ica,
-                                       status, message,
-                                       cxx_modules_directory)) {
+                                       status, message, cxxModulesDirectory)) {
             return false;
           }
         }
@@ -2303,8 +2301,8 @@
     cm::make_unique<cmInstallCMakeConfigExportGenerator>(
       &exportSet, ica.GetDestination(), ica.GetPermissions(),
       ica.GetConfigurations(), ica.GetComponent(), message,
-      ica.GetExcludeFromAll(), std::move(fname), std::move(name_space),
-      std::move(cxx_modules_directory), exportOld, exportPackageDependencies,
+      ica.GetExcludeFromAll(), std::move(fname), std::move(exportNamespace),
+      std::move(cxxModulesDirectory), exportOld, exportPackageDependencies,
       helper.Makefile->GetBacktrace()));
 
   return true;
diff --git a/Source/cmInstallCxxModuleBmiGenerator.cxx b/Source/cmInstallCxxModuleBmiGenerator.cxx
index 66f39a4..99e32cb 100644
--- a/Source/cmInstallCxxModuleBmiGenerator.cxx
+++ b/Source/cmInstallCxxModuleBmiGenerator.cxx
@@ -14,14 +14,14 @@
 #include "cmStringAlgorithms.h"
 
 cmInstallCxxModuleBmiGenerator::cmInstallCxxModuleBmiGenerator(
-  std::string target, std::string const& dest, std::string file_permissions,
+  std::string target, std::string const& dest, std::string filePermissions,
   std::vector<std::string> const& configurations, std::string const& component,
-  MessageLevel message, bool exclude_from_all, bool optional,
+  MessageLevel message, bool excludeFromAll, bool optional,
   cmListFileBacktrace backtrace)
   : cmInstallGenerator(dest, configurations, component, message,
-                       exclude_from_all, false, std::move(backtrace))
+                       excludeFromAll, false, std::move(backtrace))
   , TargetName(std::move(target))
-  , FilePermissions(std::move(file_permissions))
+  , FilePermissions(std::move(filePermissions))
   , Optional(optional)
 {
   this->ActionsPerConfig = true;
@@ -46,12 +46,12 @@
 std::string cmInstallCxxModuleBmiGenerator::GetScriptLocation(
   std::string const& config) const
 {
-  char const* config_name = config.c_str();
+  char const* configName = config.c_str();
   if (config.empty()) {
-    config_name = "noconfig";
+    configName = "noconfig";
   }
   return cmStrCat(this->Target->GetCMFSupportDirectory(),
-                  "/install-cxx-module-bmi-", config_name, ".cmake");
+                  "/install-cxx-module-bmi-", configName, ".cmake");
 }
 
 std::string cmInstallCxxModuleBmiGenerator::GetDestination(
diff --git a/Source/cmInstallCxxModuleBmiGenerator.h b/Source/cmInstallCxxModuleBmiGenerator.h
index b3b5d37..28ed2b5 100644
--- a/Source/cmInstallCxxModuleBmiGenerator.h
+++ b/Source/cmInstallCxxModuleBmiGenerator.h
@@ -21,9 +21,9 @@
 {
 public:
   cmInstallCxxModuleBmiGenerator(
-    std::string target, std::string const& dest, std::string file_permissions,
+    std::string target, std::string const& dest, std::string filePermissions,
     std::vector<std::string> const& configurations,
-    std::string const& component, MessageLevel message, bool exclude_from_all,
+    std::string const& component, MessageLevel message, bool excludeFromAll,
     bool optional, cmListFileBacktrace backtrace);
   ~cmInstallCxxModuleBmiGenerator() override;
 
diff --git a/Source/cmInstallDirectoryGenerator.cxx b/Source/cmInstallDirectoryGenerator.cxx
index bd62dd3..96d32ba 100644
--- a/Source/cmInstallDirectoryGenerator.cxx
+++ b/Source/cmInstallDirectoryGenerator.cxx
@@ -16,16 +16,16 @@
 
 cmInstallDirectoryGenerator::cmInstallDirectoryGenerator(
   std::vector<std::string> const& dirs, std::string const& dest,
-  std::string file_permissions, std::string dir_permissions,
+  std::string filePermissions, std::string dirPermissions,
   std::vector<std::string> const& configurations, std::string const& component,
-  MessageLevel message, bool exclude_from_all, std::string literal_args,
+  MessageLevel message, bool excludeFromAll, std::string literalArgs,
   bool optional, cmListFileBacktrace backtrace)
   : cmInstallGenerator(dest, configurations, component, message,
-                       exclude_from_all, false, std::move(backtrace))
+                       excludeFromAll, false, std::move(backtrace))
   , Directories(dirs)
-  , FilePermissions(std::move(file_permissions))
-  , DirPermissions(std::move(dir_permissions))
-  , LiteralArguments(std::move(literal_args))
+  , FilePermissions(std::move(filePermissions))
+  , DirPermissions(std::move(dirPermissions))
+  , LiteralArguments(std::move(literalArgs))
   , Optional(optional)
 {
   // We need per-config actions if destination have generator expressions.
@@ -107,11 +107,11 @@
   std::vector<std::string> const& dirs)
 {
   // Write code to install the directories.
-  char const* no_rename = nullptr;
+  char const* noRename = nullptr;
   this->AddInstallRule(os, this->GetDestination(config),
                        cmInstallType_DIRECTORY, dirs, this->Optional,
                        this->FilePermissions.c_str(),
-                       this->DirPermissions.c_str(), no_rename,
+                       this->DirPermissions.c_str(), noRename,
                        this->LiteralArguments.c_str(), indent);
 }
 
diff --git a/Source/cmInstallDirectoryGenerator.h b/Source/cmInstallDirectoryGenerator.h
index b02824a..d228f9b 100644
--- a/Source/cmInstallDirectoryGenerator.h
+++ b/Source/cmInstallDirectoryGenerator.h
@@ -21,10 +21,10 @@
 public:
   cmInstallDirectoryGenerator(
     std::vector<std::string> const& dirs, std::string const& dest,
-    std::string file_permissions, std::string dir_permissions,
+    std::string filePermissions, std::string dirPermissions,
     std::vector<std::string> const& configurations,
-    std::string const& component, MessageLevel message, bool exclude_from_all,
-    std::string literal_args, bool optional, cmListFileBacktrace backtrace);
+    std::string const& component, MessageLevel message, bool excludeFromAll,
+    std::string literalArgs, bool optional, cmListFileBacktrace backtrace);
   ~cmInstallDirectoryGenerator() override;
 
   bool Compute(cmLocalGenerator* lg) override;
diff --git a/Source/cmInstallExportGenerator.cxx b/Source/cmInstallExportGenerator.cxx
index 8886439..7f2474d 100644
--- a/Source/cmInstallExportGenerator.cxx
+++ b/Source/cmInstallExportGenerator.cxx
@@ -122,8 +122,8 @@
   std::vector<std::string> files;
   for (auto const& i : this->EFGen->GetConfigImportFiles()) {
     files.push_back(i.second);
-    std::string config_test = this->CreateConfigTest(i.first);
-    os << indent << "if(" << config_test << ")\n";
+    std::string configTest = this->CreateConfigTest(i.first);
+    os << indent << "if(" << configTest << ")\n";
     this->AddInstallRule(os, this->Destination, cmInstallType_FILES, files,
                          false, this->FilePermissions.c_str(), nullptr,
                          nullptr, nullptr, indent.Next());
@@ -182,8 +182,8 @@
   }
   for (auto const& i : this->EFGen->GetConfigCxxModuleFiles()) {
     files.push_back(i.second);
-    std::string config_test = this->CreateConfigTest(i.first);
-    os << indent << "if(" << config_test << ")\n";
+    std::string configTest = this->CreateConfigTest(i.first);
+    os << indent << "if(" << configTest << ")\n";
     this->AddInstallRule(os, cxxModuleDestination, cmInstallType_FILES, files,
                          false, this->FilePermissions.c_str(), nullptr,
                          nullptr, nullptr, indent.Next());
@@ -191,8 +191,8 @@
     files.clear();
   }
   for (auto const& i : this->EFGen->GetConfigCxxModuleTargetFiles()) {
-    std::string config_test = this->CreateConfigTest(i.first);
-    os << indent << "if(" << config_test << ")\n";
+    std::string configTest = this->CreateConfigTest(i.first);
+    os << indent << "if(" << configTest << ")\n";
     this->AddInstallRule(os, cxxModuleDestination, cmInstallType_FILES,
                          i.second, false, this->FilePermissions.c_str(),
                          nullptr, nullptr, nullptr, indent.Next());
diff --git a/Source/cmInstallFileSetGenerator.cxx b/Source/cmInstallFileSetGenerator.cxx
index e6e9f03..60bacc9 100644
--- a/Source/cmInstallFileSetGenerator.cxx
+++ b/Source/cmInstallFileSetGenerator.cxx
@@ -29,14 +29,14 @@
 
 cmInstallFileSetGenerator::cmInstallFileSetGenerator(
   std::string targetName, std::string fileSetName, std::string destination,
-  std::string file_permissions, std::vector<std::string> const& configurations,
-  std::string const& component, MessageLevel message, bool exclude_from_all,
+  std::string filePermissions, std::vector<std::string> const& configurations,
+  std::string const& component, MessageLevel message, bool excludeFromAll,
   bool optional, cmListFileBacktrace backtrace)
   : cmInstallGenerator(std::move(destination), configurations, component,
-                       message, exclude_from_all, false, std::move(backtrace))
+                       message, excludeFromAll, false, std::move(backtrace))
   , TargetName(std::move(targetName))
   , FileSetName(std::move(fileSetName))
-  , FilePermissions(std::move(file_permissions))
+  , FilePermissions(std::move(filePermissions))
   , Optional(optional)
 {
   this->ActionsPerConfig = true;
diff --git a/Source/cmInstallFileSetGenerator.h b/Source/cmInstallFileSetGenerator.h
index b0488fb..8586692 100644
--- a/Source/cmInstallFileSetGenerator.h
+++ b/Source/cmInstallFileSetGenerator.h
@@ -20,10 +20,10 @@
 public:
   cmInstallFileSetGenerator(std::string targetName, std::string fileSetName,
                             std::string destination,
-                            std::string file_permissions,
+                            std::string filePermissions,
                             std::vector<std::string> const& configurations,
                             std::string const& component, MessageLevel message,
-                            bool exclude_from_all, bool optional,
+                            bool excludeFromAll, bool optional,
                             cmListFileBacktrace backtrace);
   ~cmInstallFileSetGenerator() override;
 
diff --git a/Source/cmInstallFilesGenerator.cxx b/Source/cmInstallFilesGenerator.cxx
index f5613fa..bc9517a 100644
--- a/Source/cmInstallFilesGenerator.cxx
+++ b/Source/cmInstallFilesGenerator.cxx
@@ -11,14 +11,14 @@
 
 cmInstallFilesGenerator::cmInstallFilesGenerator(
   std::vector<std::string> const& files, std::string const& dest,
-  bool programs, std::string file_permissions,
+  bool programs, std::string filePermissions,
   std::vector<std::string> const& configurations, std::string const& component,
-  MessageLevel message, bool exclude_from_all, std::string rename,
-  bool optional, cmListFileBacktrace backtrace)
+  MessageLevel message, bool excludeFromAll, std::string rename, bool optional,
+  cmListFileBacktrace backtrace)
   : cmInstallGenerator(dest, configurations, component, message,
-                       exclude_from_all, false, std::move(backtrace))
+                       excludeFromAll, false, std::move(backtrace))
   , Files(files)
-  , FilePermissions(std::move(file_permissions))
+  , FilePermissions(std::move(filePermissions))
   , Rename(std::move(rename))
   , Programs(programs)
   , Optional(optional)
@@ -86,11 +86,11 @@
   std::vector<std::string> const& files)
 {
   // Write code to install the files.
-  char const* no_dir_permissions = nullptr;
+  char const* noDirPermissions = nullptr;
   this->AddInstallRule(
     os, this->GetDestination(config),
     (this->Programs ? cmInstallType_PROGRAMS : cmInstallType_FILES), files,
-    this->Optional, this->FilePermissions.c_str(), no_dir_permissions,
+    this->Optional, this->FilePermissions.c_str(), noDirPermissions,
     this->GetRename(config).c_str(), nullptr, indent);
 }
 
diff --git a/Source/cmInstallFilesGenerator.h b/Source/cmInstallFilesGenerator.h
index 5ab2f50..c8f4842 100644
--- a/Source/cmInstallFilesGenerator.h
+++ b/Source/cmInstallFilesGenerator.h
@@ -21,10 +21,10 @@
 public:
   cmInstallFilesGenerator(std::vector<std::string> const& files,
                           std::string const& dest, bool programs,
-                          std::string file_permissions,
+                          std::string filePermissions,
                           std::vector<std::string> const& configurations,
                           std::string const& component, MessageLevel message,
-                          bool exclude_from_all, std::string rename,
+                          bool excludeFromAll, std::string rename,
                           bool optional, cmListFileBacktrace backtrace);
   ~cmInstallFilesGenerator() override;
 
diff --git a/Source/cmInstallGenerator.cxx b/Source/cmInstallGenerator.cxx
index 9317f94..585bf80 100644
--- a/Source/cmInstallGenerator.cxx
+++ b/Source/cmInstallGenerator.cxx
@@ -16,14 +16,14 @@
 
 cmInstallGenerator::cmInstallGenerator(
   std::string destination, std::vector<std::string> const& configurations,
-  std::string component, MessageLevel message, bool exclude_from_all,
-  bool all_components, cmListFileBacktrace backtrace)
+  std::string component, MessageLevel message, bool excludeFromAll,
+  bool allComponents, cmListFileBacktrace backtrace)
   : cmScriptGenerator("CMAKE_INSTALL_CONFIG_NAME", configurations)
   , Destination(std::move(destination))
   , Component(std::move(component))
   , Message(message)
-  , ExcludeFromAll(exclude_from_all)
-  , AllComponents(all_components)
+  , ExcludeFromAll(excludeFromAll)
+  , AllComponents(allComponents)
   , Backtrace(std::move(backtrace))
 {
 }
@@ -46,10 +46,10 @@
 void cmInstallGenerator::AddInstallRule(
   std::ostream& os, std::string const& dest, cmInstallType type,
   std::vector<std::string> const& files, bool optional /* = false */,
-  char const* permissions_file /* = nullptr */,
-  char const* permissions_dir /* = nullptr */,
-  char const* rename /* = nullptr */, char const* literal_args /* = nullptr */,
-  Indent indent, char const* files_var /* = nullptr */)
+  char const* permissionsFile /* = nullptr */,
+  char const* permissionsDir /* = nullptr */,
+  char const* rename /* = nullptr */, char const* literalArgs /* = nullptr */,
+  Indent indent, char const* filesVar /* = nullptr */)
 {
   // Use the FILE command to install the file.
   std::string stype;
@@ -95,9 +95,8 @@
       }
       os << "\")\n";
     }
-    if (files_var) {
-      os << indent << "foreach(_cmake_abs_file IN LISTS " << files_var
-         << ")\n";
+    if (filesVar) {
+      os << indent << "foreach(_cmake_abs_file IN LISTS " << filesVar << ")\n";
       os << indent.Next()
          << "get_filename_component(_cmake_abs_file_name "
             "\"${_cmake_abs_file}\" NAME)\n";
@@ -139,11 +138,11 @@
       os << " MESSAGE_NEVER";
       break;
   }
-  if (permissions_file && *permissions_file) {
-    os << " PERMISSIONS" << permissions_file;
+  if (permissionsFile && *permissionsFile) {
+    os << " PERMISSIONS" << permissionsFile;
   }
-  if (permissions_dir && *permissions_dir) {
-    os << " DIR_PERMISSIONS" << permissions_dir;
+  if (permissionsDir && *permissionsDir) {
+    os << " DIR_PERMISSIONS" << permissionsDir;
   }
   if (rename && *rename) {
     os << " RENAME \"" << rename << "\"";
@@ -155,25 +154,25 @@
     for (std::string const& f : files) {
       os << "\n" << indent << "  \"" << f << "\"";
     }
-    if (files_var) {
-      os << " ${" << files_var << "}";
+    if (filesVar) {
+      os << " ${" << filesVar << "}";
     }
     os << "\n" << indent << " ";
-    if (!(literal_args && *literal_args)) {
+    if (!(literalArgs && *literalArgs)) {
       os << " ";
     }
   }
-  if (literal_args && *literal_args) {
-    os << literal_args;
+  if (literalArgs && *literalArgs) {
+    os << literalArgs;
   }
   os << ")\n";
 }
 
 std::string cmInstallGenerator::CreateComponentTest(
-  std::string const& component, bool exclude_from_all, bool all_components)
+  std::string const& component, bool excludeFromAll, bool allComponents)
 {
-  if (all_components) {
-    if (exclude_from_all) {
+  if (allComponents) {
+    if (excludeFromAll) {
       return "CMAKE_INSTALL_COMPONENT";
     }
     return {};
@@ -182,7 +181,7 @@
   std::string result = "CMAKE_INSTALL_COMPONENT STREQUAL \"";
   result += component;
   result += "\"";
-  if (!exclude_from_all) {
+  if (!excludeFromAll) {
     result += " OR NOT CMAKE_INSTALL_COMPONENT";
   }
 
@@ -194,12 +193,12 @@
   // Track indentation.
   Indent indent;
 
-  std::string component_test = this->CreateComponentTest(
+  std::string componentTest = this->CreateComponentTest(
     this->Component, this->ExcludeFromAll, this->AllComponents);
 
   // Begin this block of installation.
-  if (!component_test.empty()) {
-    os << indent << "if(" << component_test << ")\n";
+  if (!componentTest.empty()) {
+    os << indent << "if(" << componentTest << ")\n";
   }
 
   // Generate the script possibly with per-configuration code.
@@ -207,7 +206,7 @@
                               this->AllComponents ? indent : indent.Next());
 
   // End this block of installation.
-  if (!component_test.empty()) {
+  if (!componentTest.empty()) {
     os << indent << "endif()\n\n";
   }
 }
diff --git a/Source/cmInstallGenerator.h b/Source/cmInstallGenerator.h
index d35a4b6..9a4b68b 100644
--- a/Source/cmInstallGenerator.h
+++ b/Source/cmInstallGenerator.h
@@ -34,7 +34,7 @@
   cmInstallGenerator(std::string destination,
                      std::vector<std::string> const& configurations,
                      std::string component, MessageLevel message,
-                     bool exclude_from_all, bool all_components,
+                     bool excludeFromAll, bool allComponents,
                      cmListFileBacktrace backtrace);
   ~cmInstallGenerator() override;
 
@@ -48,10 +48,10 @@
   void AddInstallRule(
     std::ostream& os, std::string const& dest, cmInstallType type,
     std::vector<std::string> const& files, bool optional = false,
-    char const* permissions_file = nullptr,
-    char const* permissions_dir = nullptr, char const* rename = nullptr,
-    char const* literal_args = nullptr, Indent indent = Indent(),
-    char const* files_var = nullptr);
+    char const* permissionsFile = nullptr,
+    char const* permissionsDir = nullptr, char const* rename = nullptr,
+    char const* literalArgs = nullptr, Indent indent = Indent(),
+    char const* filesVar = nullptr);
 
   /** Get the install destination as it should appear in the
       installation script.  */
@@ -81,8 +81,8 @@
   void GenerateScript(std::ostream& os) override;
 
   std::string CreateComponentTest(std::string const& component,
-                                  bool exclude_from_all,
-                                  bool all_components = false);
+                                  bool excludeFromAll,
+                                  bool allComponents = false);
 
   using TweakMethod =
     std::function<void(std::ostream& os, Indent indent,
diff --git a/Source/cmInstallGetRuntimeDependenciesGenerator.cxx b/Source/cmInstallGetRuntimeDependenciesGenerator.cxx
index 4292f8f..6e27498 100644
--- a/Source/cmInstallGetRuntimeDependenciesGenerator.cxx
+++ b/Source/cmInstallGetRuntimeDependenciesGenerator.cxx
@@ -84,10 +84,10 @@
     std::vector<std::string> postExcludeFiles, std::string libraryComponent,
     std::string frameworkComponent, bool noInstallRPath, char const* depsVar,
     char const* rpathPrefix, std::vector<std::string> const& configurations,
-    MessageLevel message, bool exclude_from_all, cmListFileBacktrace backtrace,
+    MessageLevel message, bool excludeFromAll, cmListFileBacktrace backtrace,
     cmPolicies::PolicyStatus policyStatusCMP0207)
-  : cmInstallGenerator("", configurations, "", message, exclude_from_all,
-                       false, std::move(backtrace))
+  : cmInstallGenerator("", configurations, "", message, excludeFromAll, false,
+                       std::move(backtrace))
   , RuntimeDependencySet(runtimeDependencySet)
   , Directories(std::move(directories))
   , PreIncludeRegexes(std::move(preIncludeRegexes))
diff --git a/Source/cmInstallGetRuntimeDependenciesGenerator.h b/Source/cmInstallGetRuntimeDependenciesGenerator.h
index 9670dcf..413d2b8 100644
--- a/Source/cmInstallGetRuntimeDependenciesGenerator.h
+++ b/Source/cmInstallGetRuntimeDependenciesGenerator.h
@@ -27,7 +27,7 @@
     std::vector<std::string> postExcludeFiles, std::string libraryComponent,
     std::string frameworkComponent, bool noInstallRPath, char const* depsVar,
     char const* rpathPrefix, std::vector<std::string> const& configurations,
-    MessageLevel message, bool exclude_from_all, cmListFileBacktrace backtrace,
+    MessageLevel message, bool excludeFromAll, cmListFileBacktrace backtrace,
     cmPolicies::PolicyStatus policyStatusCMP0207);
 
   bool Compute(cmLocalGenerator* lg) override;
diff --git a/Source/cmInstallImportedRuntimeArtifactsGenerator.cxx b/Source/cmInstallImportedRuntimeArtifactsGenerator.cxx
index 9552748..a2ae0ff 100644
--- a/Source/cmInstallImportedRuntimeArtifactsGenerator.cxx
+++ b/Source/cmInstallImportedRuntimeArtifactsGenerator.cxx
@@ -32,14 +32,14 @@
 cmInstallImportedRuntimeArtifactsGenerator::
   cmInstallImportedRuntimeArtifactsGenerator(
     std::string targetName, std::string const& dest,
-    std::string file_permissions,
+    std::string filePermissions,
     std::vector<std::string> const& configurations,
-    std::string const& component, MessageLevel message, bool exclude_from_all,
+    std::string const& component, MessageLevel message, bool excludeFromAll,
     bool optional, cmListFileBacktrace backtrace)
   : cmInstallGenerator(dest, configurations, component, message,
-                       exclude_from_all, false, std::move(backtrace))
+                       excludeFromAll, false, std::move(backtrace))
   , TargetName(std::move(targetName))
-  , FilePermissions(std::move(file_permissions))
+  , FilePermissions(std::move(filePermissions))
   , Optional(optional)
 {
   this->ActionsPerConfig = true;
diff --git a/Source/cmInstallImportedRuntimeArtifactsGenerator.h b/Source/cmInstallImportedRuntimeArtifactsGenerator.h
index 90c13a8..411d472 100644
--- a/Source/cmInstallImportedRuntimeArtifactsGenerator.h
+++ b/Source/cmInstallImportedRuntimeArtifactsGenerator.h
@@ -16,9 +16,9 @@
 public:
   cmInstallImportedRuntimeArtifactsGenerator(
     std::string targetName, std::string const& dest,
-    std::string file_permissions,
+    std::string filePermissions,
     std::vector<std::string> const& configurations,
-    std::string const& component, MessageLevel message, bool exclude_from_all,
+    std::string const& component, MessageLevel message, bool excludeFromAll,
     bool optional, cmListFileBacktrace backtrace = cmListFileBacktrace());
   ~cmInstallImportedRuntimeArtifactsGenerator() override = default;
 
diff --git a/Source/cmInstallProgramsCommand.cxx b/Source/cmInstallProgramsCommand.cxx
index 63f40e2..0a808b7 100644
--- a/Source/cmInstallProgramsCommand.cxx
+++ b/Source/cmInstallProgramsCommand.cxx
@@ -48,18 +48,18 @@
 static void FinalAction(cmMakefile& makefile, std::string const& dest,
                         std::vector<std::string> const& args)
 {
-  bool files_mode = false;
+  bool filesMode = false;
   if (!args.empty() && args[0] == "FILES") {
-    files_mode = true;
+    filesMode = true;
   }
 
   std::vector<std::string> files;
 
   // two different options
-  if (args.size() > 1 || files_mode) {
+  if (args.size() > 1 || filesMode) {
     // for each argument, get the programs
     auto s = args.begin();
-    if (files_mode) {
+    if (filesMode) {
       // Skip the FILES argument in files mode.
       ++s;
     }
diff --git a/Source/cmInstallRuntimeDependencySetGenerator.cxx b/Source/cmInstallRuntimeDependencySetGenerator.cxx
index ec46045..53c3429 100644
--- a/Source/cmInstallRuntimeDependencySetGenerator.cxx
+++ b/Source/cmInstallRuntimeDependencySetGenerator.cxx
@@ -24,10 +24,10 @@
   std::string installNameDir, bool noInstallName, char const* depsVar,
   char const* rpathPrefix, char const* tmpVarPrefix, std::string destination,
   std::vector<std::string> const& configurations, std::string component,
-  std::string permissions, MessageLevel message, bool exclude_from_all,
+  std::string permissions, MessageLevel message, bool excludeFromAll,
   cmListFileBacktrace backtrace)
   : cmInstallGenerator(std::move(destination), configurations,
-                       std::move(component), message, exclude_from_all, false,
+                       std::move(component), message, excludeFromAll, false,
                        std::move(backtrace))
   , Type(type)
   , DependencySet(dependencySet)
diff --git a/Source/cmInstallRuntimeDependencySetGenerator.h b/Source/cmInstallRuntimeDependencySetGenerator.h
index f8a7a4f..80f4cc3 100644
--- a/Source/cmInstallRuntimeDependencySetGenerator.h
+++ b/Source/cmInstallRuntimeDependencySetGenerator.h
@@ -27,7 +27,7 @@
     std::string installNameDir, bool noInstallName, char const* depsVar,
     char const* rpathPrefix, char const* tmpVarPrefix, std::string destination,
     std::vector<std::string> const& configurations, std::string component,
-    std::string permissions, MessageLevel message, bool exclude_from_all,
+    std::string permissions, MessageLevel message, bool excludeFromAll,
     cmListFileBacktrace backtrace);
 
   bool Compute(cmLocalGenerator* lg) override;
diff --git a/Source/cmInstallScriptGenerator.cxx b/Source/cmInstallScriptGenerator.cxx
index a75e924..0070b60 100644
--- a/Source/cmInstallScriptGenerator.cxx
+++ b/Source/cmInstallScriptGenerator.cxx
@@ -14,9 +14,9 @@
 
 cmInstallScriptGenerator::cmInstallScriptGenerator(
   std::string script, bool code, std::string const& component,
-  bool exclude_from_all, bool all_components, cmListFileBacktrace backtrace)
+  bool excludeFromAll, bool allComponents, cmListFileBacktrace backtrace)
   : cmInstallGenerator("", std::vector<std::string>(), component,
-                       MessageDefault, exclude_from_all, all_components,
+                       MessageDefault, excludeFromAll, allComponents,
                        std::move(backtrace))
   , Script(std::move(script))
   , Code(code)
diff --git a/Source/cmInstallScriptGenerator.h b/Source/cmInstallScriptGenerator.h
index 104d432..fe7ca8f 100644
--- a/Source/cmInstallScriptGenerator.h
+++ b/Source/cmInstallScriptGenerator.h
@@ -20,7 +20,7 @@
 public:
   cmInstallScriptGenerator(
     std::string script, bool code, std::string const& component,
-    bool exclude_from_all, bool all_components,
+    bool excludeFromAll, bool allComponents,
     cmListFileBacktrace backtrace = cmListFileBacktrace());
   ~cmInstallScriptGenerator() override;
 
diff --git a/Source/cmInstallScriptHandler.cxx b/Source/cmInstallScriptHandler.cxx
index 6a268f9..99a0694 100644
--- a/Source/cmInstallScriptHandler.cxx
+++ b/Source/cmInstallScriptHandler.cxx
@@ -34,86 +34,86 @@
 using InstallScriptRunner = cmInstallScriptHandler::InstallScriptRunner;
 
 cmInstallScriptHandler::cmInstallScriptHandler(
-  std::string _binaryDir, std::vector<std::string> _components,
-  std::string _config, std::vector<std::string>& args)
-  : components(std::move(_components))
-  , binaryDir(std::move(_binaryDir))
+  std::string binaryDir, std::vector<std::string> components,
+  std::string installConfig, std::vector<std::string>& args)
+  : Components(std::move(components))
+  , BinaryDir(std::move(binaryDir))
 {
-  if (this->components.empty()) {
-    this->components.emplace_back(std::string{});
+  if (this->Components.empty()) {
+    this->Components.emplace_back(std::string{});
   }
 
   std::string const& file =
-    cmStrCat(this->binaryDir, "/CMakeFiles/InstallScripts.json");
-  this->parallel = false;
+    cmStrCat(this->BinaryDir, "/CMakeFiles/InstallScripts.json");
+  this->Parallel = false;
 
   auto addScript = [this, &args](std::string script, std::string component,
                                  std::string config) -> void {
-    this->scripts.push_back({ script, config, args });
+    this->Scripts.push_back({ script, config, args });
     if (!component.empty()) {
-      this->scripts.back().command.insert(
-        this->scripts.back().command.end() - 1,
+      this->Scripts.back().command.insert(
+        this->Scripts.back().command.end() - 1,
         cmStrCat("-DCMAKE_INSTALL_COMPONENT=", component));
     }
     if (!config.empty()) {
-      this->scripts.back().command.insert(
-        this->scripts.back().command.end() - 1,
+      this->Scripts.back().command.insert(
+        this->Scripts.back().command.end() - 1,
         cmStrCat("-DCMAKE_INSTALL_CONFIG_NAME=", config));
     }
-    this->scripts.back().command.emplace_back(script);
-    this->directories.push_back(cmSystemTools::GetFilenamePath(script));
+    this->Scripts.back().command.emplace_back(script);
+    this->Directories.push_back(cmSystemTools::GetFilenamePath(script));
   };
 
   int compare = 1;
   if (cmSystemTools::FileExists(file)) {
     cmSystemTools::FileTimeCompare(
-      cmStrCat(this->binaryDir, "/CMakeFiles/cmake.check_cache"), file,
+      cmStrCat(this->BinaryDir, "/CMakeFiles/cmake.check_cache"), file,
       &compare);
   }
   if (compare < 1) {
     Json::CharReaderBuilder rbuilder;
-    auto JsonReader =
+    auto jsonReader =
       std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
     std::vector<char> content;
     Json::Value value;
     cmJSONState state(file, &value);
-    this->parallel = value["Parallel"].asBool();
-    if (this->parallel) {
+    this->Parallel = value["Parallel"].asBool();
+    if (this->Parallel) {
       args.insert(args.end() - 1, "-DCMAKE_INSTALL_LOCAL_ONLY=1");
     }
-    if (_config.empty() && value.isMember("Configs")) {
+    if (installConfig.empty() && value.isMember("Configs")) {
       for (auto const& config : value["Configs"]) {
-        this->configs.push_back(config.asCString());
+        this->Configs.push_back(config.asCString());
       }
     } else {
-      this->configs.push_back(_config);
+      this->Configs.push_back(installConfig);
     }
     for (auto const& script : value["InstallScripts"]) {
-      for (auto const& component : components) {
-        for (auto const& config : configs) {
+      for (auto const& component : Components) {
+        for (auto const& config : Configs) {
           addScript(script.asCString(), component, config);
         }
       }
-      if (!this->parallel) {
+      if (!this->Parallel) {
         break;
       }
     }
   } else {
-    for (auto const& component : components) {
-      addScript(cmStrCat(this->binaryDir, "/cmake_install.cmake"), component,
-                _config);
+    for (auto const& component : Components) {
+      addScript(cmStrCat(this->BinaryDir, "/cmake_install.cmake"), component,
+                installConfig);
     }
   }
 }
 
 bool cmInstallScriptHandler::IsParallel()
 {
-  return this->parallel;
+  return this->Parallel;
 }
 
 std::vector<InstallScript> cmInstallScriptHandler::GetScripts() const
 {
-  return this->scripts;
+  return this->Scripts;
 }
 
 int cmInstallScriptHandler::Install(unsigned int j,
@@ -122,27 +122,27 @@
   cm::uv_loop_ptr loop;
   loop.init();
   std::vector<InstallScriptRunner> runners;
-  runners.reserve(this->scripts.size());
+  runners.reserve(this->Scripts.size());
 
-  std::vector<std::string> instrument_arg;
+  std::vector<std::string> instrumentArg;
   if (instrumentation.HasQuery()) {
-    instrument_arg = { cmSystemTools::GetCTestCommand(),
-                       "--instrument",
-                       "--command-type",
-                       "install",
-                       "--build-dir",
-                       this->binaryDir,
-                       "--config",
-                       "",
-                       "--" };
+    instrumentArg = { cmSystemTools::GetCTestCommand(),
+                      "--instrument",
+                      "--command-type",
+                      "install",
+                      "--build-dir",
+                      this->BinaryDir,
+                      "--config",
+                      "",
+                      "--" };
   }
 
-  for (auto& script : this->scripts) {
-    if (!instrument_arg.empty()) {
-      instrument_arg[7] = script.config; // --config <script.config>
+  for (auto& script : this->Scripts) {
+    if (!instrumentArg.empty()) {
+      instrumentArg[7] = script.config; // --config <script.config>
     }
-    script.command.insert(script.command.begin(), instrument_arg.begin(),
-                          instrument_arg.end());
+    script.command.insert(script.command.begin(), instrumentArg.begin(),
+                          instrumentArg.end());
     runners.emplace_back(script);
   }
   std::size_t working = 0;
@@ -168,28 +168,28 @@
   uv_run(loop, UV_RUN_DEFAULT);
 
   // Write install manifest
-  std::string install_manifest;
-  for (auto const& component : this->components) {
+  std::string installManifest;
+  for (auto const& component : this->Components) {
     if (component.empty()) {
-      install_manifest = "install_manifest.txt";
+      installManifest = "install_manifest.txt";
     } else {
       cmsys::RegularExpression regEntry;
       if (regEntry.compile("^[a-zA-Z0-9_.+-]+$") && regEntry.find(component)) {
-        install_manifest = cmStrCat("install_manifest_", component, ".txt");
+        installManifest = cmStrCat("install_manifest_", component, ".txt");
       } else {
         cmCryptoHash md5(cmCryptoHash::AlgoMD5);
         md5.Initialize();
-        install_manifest =
+        installManifest =
           cmStrCat("install_manifest_", md5.HashString(component), ".txt");
       }
     }
     cmGeneratedFileStream fout(
-      cmStrCat(this->binaryDir, '/', install_manifest));
+      cmStrCat(this->BinaryDir, '/', installManifest));
     fout.SetCopyIfDifferent(true);
-    for (auto const& dir : this->directories) {
-      auto local_manifest = cmStrCat(dir, "/install_local_manifest.txt");
-      if (cmSystemTools::FileExists(local_manifest)) {
-        cmsys::ifstream fin(local_manifest.c_str());
+    for (auto const& dir : this->Directories) {
+      auto localManifest = cmStrCat(dir, "/install_local_manifest.txt");
+      if (cmSystemTools::FileExists(localManifest)) {
+        cmsys::ifstream fin(localManifest.c_str());
         std::string line;
         while (std::getline(fin, line)) {
           fout << line << "\n";
@@ -202,34 +202,34 @@
 
 InstallScriptRunner::InstallScriptRunner(InstallScript const& script)
 {
-  this->name = cmSystemTools::RelativePath(
+  this->Name = cmSystemTools::RelativePath(
     cmSystemTools::GetLogicalWorkingDirectory(), script.path);
-  this->command = script.command;
+  this->Command = script.command;
 }
 
 void InstallScriptRunner::start(cm::uv_loop_ptr& loop,
                                 std::function<void()> callback)
 {
   cmUVProcessChainBuilder builder;
-  builder.AddCommand(this->command)
+  builder.AddCommand(this->Command)
     .SetExternalLoop(*loop)
     .SetMergedBuiltinStreams();
-  this->chain = cm::make_unique<cmUVProcessChain>(builder.Start());
-  this->streamHandler = cmUVStreamRead(
-    this->chain->OutputStream(),
+  this->Chain = cm::make_unique<cmUVProcessChain>(builder.Start());
+  this->StreamHandler = cmUVStreamRead(
+    this->Chain->OutputStream(),
     [this](std::vector<char> data) {
       std::string strdata;
       cmProcessOutput(cmProcessOutput::Auto)
         .DecodeText(data.data(), data.size(), strdata);
-      this->output.push_back(strdata);
+      this->Output.push_back(strdata);
     },
     std::move(callback));
 }
 
 void InstallScriptRunner::printResult(std::size_t n, std::size_t total)
 {
-  cmSystemTools::Stdout(cmStrCat('[', n, '/', total, "] ", this->name, '\n'));
-  for (auto const& line : this->output) {
+  cmSystemTools::Stdout(cmStrCat('[', n, '/', total, "] ", this->Name, '\n'));
+  for (auto const& line : this->Output) {
     cmSystemTools::Stdout(line);
   }
 }
diff --git a/Source/cmInstallScriptHandler.h b/Source/cmInstallScriptHandler.h
index 3fcfac0..9ab8f8e 100644
--- a/Source/cmInstallScriptHandler.h
+++ b/Source/cmInstallScriptHandler.h
@@ -40,18 +40,18 @@
     void printResult(std::size_t n, std::size_t total);
 
   private:
-    std::vector<std::string> command;
-    std::vector<std::string> output;
-    std::string name;
-    std::unique_ptr<cmUVProcessChain> chain;
-    std::unique_ptr<cmUVStreamReadHandle> streamHandler;
+    std::vector<std::string> Command;
+    std::vector<std::string> Output;
+    std::string Name;
+    std::unique_ptr<cmUVProcessChain> Chain;
+    std::unique_ptr<cmUVStreamReadHandle> StreamHandler;
   };
 
 private:
-  std::vector<InstallScript> scripts;
-  std::vector<std::string> configs;
-  std::vector<std::string> directories;
-  std::vector<std::string> components;
-  std::string binaryDir;
-  bool parallel;
+  std::vector<InstallScript> Scripts;
+  std::vector<std::string> Configs;
+  std::vector<std::string> Directories;
+  std::vector<std::string> Components;
+  std::string BinaryDir;
+  bool Parallel;
 };
diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx
index e2e7924..e38b296 100644
--- a/Source/cmInstallTargetGenerator.cxx
+++ b/Source/cmInstallTargetGenerator.cxx
@@ -128,13 +128,13 @@
 
 cmInstallTargetGenerator::cmInstallTargetGenerator(
   std::string targetName, std::string const& dest, bool implib,
-  std::string file_permissions, std::vector<std::string> const& configurations,
-  std::string const& component, MessageLevel message, bool exclude_from_all,
+  std::string filePermissions, std::vector<std::string> const& configurations,
+  std::string const& component, MessageLevel message, bool excludeFromAll,
   bool optional, cmListFileBacktrace backtrace)
   : cmInstallGenerator(dest, configurations, component, message,
-                       exclude_from_all, false, std::move(backtrace))
+                       excludeFromAll, false, std::move(backtrace))
   , TargetName(std::move(targetName))
-  , FilePermissions(std::move(file_permissions))
+  , FilePermissions(std::move(filePermissions))
   , ImportLibrary(implib)
   , Optional(optional)
 {
@@ -177,9 +177,9 @@
   // Write code to install the target file.
   char const* no_dir_permissions = nullptr;
   bool optional = this->Optional || this->ImportLibrary;
-  std::string literal_args;
+  std::string literalArgs;
   if (files.UseSourcePermissions) {
-    literal_args += " USE_SOURCE_PERMISSIONS";
+    literalArgs += " USE_SOURCE_PERMISSIONS";
   }
   if (files.Rename) {
     if (files.From.size() != files.To.size()) {
@@ -199,16 +199,16 @@
       }
       this->AddInstallRule(os, dest, files.Type, FileNames, optional,
                            this->FilePermissions.c_str(), no_dir_permissions,
-                           files.To[i].c_str(), literal_args.c_str(), indent);
+                           files.To[i].c_str(), literalArgs.c_str(), indent);
     }
   } else {
     char const* no_rename = nullptr;
     if (!files.FromDir.empty()) {
-      literal_args += " FILES_FROM_DIR \"" + files.FromDir + "\"";
+      literalArgs += " FILES_FROM_DIR \"" + files.FromDir + "\"";
     }
     this->AddInstallRule(os, dest, files.Type, files.From, optional,
                          this->FilePermissions.c_str(), no_dir_permissions,
-                         no_rename, literal_args.c_str(), indent);
+                         no_rename, literalArgs.c_str(), indent);
   }
 
   // Add post-installation tweaks.
diff --git a/Source/cmInstallTargetGenerator.h b/Source/cmInstallTargetGenerator.h
index 7421d0d..dd1a596 100644
--- a/Source/cmInstallTargetGenerator.h
+++ b/Source/cmInstallTargetGenerator.h
@@ -22,9 +22,9 @@
 public:
   cmInstallTargetGenerator(
     std::string targetName, std::string const& dest, bool implib,
-    std::string file_permissions,
+    std::string filePermissions,
     std::vector<std::string> const& configurations,
-    std::string const& component, MessageLevel message, bool exclude_from_all,
+    std::string const& component, MessageLevel message, bool excludeFromAll,
     bool optional, cmListFileBacktrace backtrace = cmListFileBacktrace());
   ~cmInstallTargetGenerator() override;
 
diff --git a/Source/cmInstallTargetsCommand.cxx b/Source/cmInstallTargetsCommand.cxx
index 66d40b0..6b18410 100644
--- a/Source/cmInstallTargetsCommand.cxx
+++ b/Source/cmInstallTargetsCommand.cxx
@@ -26,7 +26,7 @@
   cmMakefile::cmTargetMap& tgts = mf.GetTargets();
   auto s = args.begin();
   ++s;
-  std::string runtime_dir = "/bin";
+  std::string runtimeDir = "/bin";
   for (; s != args.end(); ++s) {
     if (*s == "RUNTIME_DIRECTORY") {
       ++s;
@@ -36,12 +36,12 @@
         return false;
       }
 
-      runtime_dir = *s;
+      runtimeDir = *s;
     } else {
       auto ti = tgts.find(*s);
       if (ti != tgts.end()) {
         ti->second.SetInstallPath(args[0]);
-        ti->second.SetRuntimeInstallPath(runtime_dir);
+        ti->second.SetRuntimeInstallPath(runtimeDir);
         ti->second.SetHaveInstallRule(true);
       } else {
         std::string str = "Cannot find target: \"" + *s + "\" to install.";
diff --git a/Source/cmList.cxx b/Source/cmList.cxx
index 4dfeaa2..5b4eecf 100644
--- a/Source/cmList.cxx
+++ b/Source/cmList.cxx
@@ -167,6 +167,60 @@
   PredicateEvaluator& Evaluator;
   bool IncludeMatches;
 };
+
+class ComparatorEvaluator
+{
+public:
+  ComparatorEvaluator(std::string functionName, cmMakefile& makefile)
+    : FunctionName(std::move(functionName))
+    , Makefile(&makefile)
+    , OutputVar(OutputVarFor("_cmake_comparator_out_", makefile))
+  {
+    if (!makefile.GetState()->GetCommand(this->FunctionName)) {
+      throw cmList::transform_error(
+        cmStrCat("sub-command SORT, COMPARATOR: unknown function \"",
+                 this->FunctionName, "\"."));
+    }
+  }
+
+  bool operator()(std::string const& a, std::string const& b)
+  {
+    this->Makefile->RemoveDefinition(this->OutputVar);
+
+    cmListFileContext context = this->Makefile->GetBacktrace().Top();
+    std::vector<cmListFileArgument> funcArgs;
+    funcArgs.emplace_back(a, cmListFileArgument::Quoted, context.Line);
+    funcArgs.emplace_back(b, cmListFileArgument::Quoted, context.Line);
+    funcArgs.emplace_back(this->OutputVar, cmListFileArgument::Quoted,
+                          context.Line);
+    cmListFileFunction func{ this->FunctionName, context.Line, context.Line,
+                             std::move(funcArgs) };
+
+    cmExecutionStatus status(*this->Makefile);
+    if (!this->Makefile->ExecuteCommand(func, status) ||
+        status.GetNestedError()) {
+      throw cmList::transform_error(
+        cmStrCat("sub-command SORT, COMPARATOR: function \"",
+                 this->FunctionName, "\" failed during execution."));
+    }
+
+    cmValue result = this->Makefile->GetDefinition(this->OutputVar);
+    if (!result) {
+      throw cmList::transform_error(
+        cmStrCat("sub-command SORT, COMPARATOR: function \"",
+                 this->FunctionName, "\" did not set the output variable."));
+    }
+
+    bool boolResult = cmIsOn(*result);
+    this->Makefile->RemoveDefinition(this->OutputVar);
+    return boolResult;
+  }
+
+private:
+  std::string FunctionName;
+  cmMakefile* Makefile = nullptr;
+  std::string OutputVar;
+};
 }
 
 cmList& cmList::filter(cm::string_view pattern, FilterMode mode)
@@ -249,6 +303,13 @@
   {
   }
 
+  StringSorter(cmList::SortConfiguration config, ComparisonFunction comparator)
+    : Filters{ nullptr, this->GetCaseFilter(config.Case) }
+    , SortMethod(std::move(comparator))
+    , Descending(config.Order == OrderMode::DESCENDING)
+  {
+  }
+
   std::string ApplyFilter(std::string const& argument)
   {
     std::string result = argument;
@@ -308,6 +369,38 @@
   return *this;
 }
 
+cmList& cmList::sort(SortConfiguration cfg, cmMakefile& makefile)
+{
+  SortConfiguration config{ cfg };
+
+  if (config.Order == SortConfiguration::OrderMode::DEFAULT) {
+    config.Order = SortConfiguration::OrderMode::ASCENDING;
+  }
+  if (config.Case == SortConfiguration::CaseSensitivity::DEFAULT) {
+    config.Case = SortConfiguration::CaseSensitivity::SENSITIVE;
+  }
+
+  try {
+    ComparatorEvaluator evaluator(config.ComparatorFunction, makefile);
+    StringSorter sorter(
+      config, [&evaluator](std::string const& a, std::string const& b) {
+        bool result = evaluator(a, b);
+        if (result && evaluator(b, a)) {
+          throw cmList::transform_error(
+            "sub-command SORT, COMPARATOR: function does not induce a strict "
+            "weak ordering. The comparator returned TRUE for both (a, b) and "
+            "(b, a).");
+        }
+        return result;
+      });
+    std::sort(this->Values.begin(), this->Values.end(), sorter);
+  } catch (transform_error& e) {
+    throw std::invalid_argument(e.what());
+  }
+
+  return *this;
+}
+
 namespace {
 using transform_type = std::function<std::string(std::string const&)>;
 using transform_error = cmList::transform_error;
diff --git a/Source/cmList.h b/Source/cmList.h
index b34a6ca..cccfb2c 100644
--- a/Source/cmList.h
+++ b/Source/cmList.h
@@ -848,6 +848,7 @@
       STRING,
       FILE_BASENAME,
       NATURAL,
+      COMPARATOR,
     } Compare = CompareMethod::DEFAULT;
 
     enum class CaseSensitivity
@@ -857,6 +858,8 @@
       INSENSITIVE,
     } Case = CaseSensitivity::DEFAULT;
 
+    std::string ComparatorFunction;
+
     // declare the default constructor to work-around clang bug
     SortConfiguration();
 
@@ -869,6 +872,7 @@
     }
   };
   cmList& sort(SortConfiguration config = SortConfiguration{});
+  cmList& sort(SortConfiguration config, cmMakefile& makefile);
 
   // exception raised on error during transform operations
   class transform_error : public std::runtime_error
diff --git a/Source/cmListCommand.cxx b/Source/cmListCommand.cxx
index e5338d2..aed1230 100644
--- a/Source/cmListCommand.cxx
+++ b/Source/cmListCommand.cxx
@@ -726,6 +726,7 @@
 
   using SortConfig = cmList::SortConfiguration;
   SortConfig sortConfig;
+  bool hasComparator = false;
 
   size_t argumentIndex = 2;
   std::string const messageHint = "sub-command SORT ";
@@ -733,6 +734,12 @@
   while (argumentIndex < args.size()) {
     std::string const& option = args[argumentIndex++];
     if (option == "COMPARE") {
+      if (hasComparator) {
+        status.SetError(cmStrCat(messageHint,
+                                 "option \"COMPARE\" is incompatible "
+                                 "with \"COMPARATOR\"."));
+        return false;
+      }
       if (sortConfig.Compare != SortConfig::CompareMethod::DEFAULT) {
         std::string error = cmStrCat(messageHint, "option \"", option,
                                      "\" has been specified multiple times.");
@@ -806,6 +813,27 @@
                                  option, "\"."));
         return false;
       }
+    } else if (option == "COMPARATOR") {
+      if (hasComparator) {
+        status.SetError(cmStrCat(messageHint, "option \"", option,
+                                 "\" has been specified multiple times."));
+        return false;
+      }
+      if (sortConfig.Compare != SortConfig::CompareMethod::DEFAULT) {
+        status.SetError(cmStrCat(messageHint,
+                                 "option \"COMPARATOR\" is incompatible "
+                                 "with \"COMPARE\"."));
+        return false;
+      }
+      if (argumentIndex < args.size()) {
+        sortConfig.ComparatorFunction = args[argumentIndex++];
+        sortConfig.Compare = SortConfig::CompareMethod::COMPARATOR;
+        hasComparator = true;
+      } else {
+        status.SetError(cmStrCat(messageHint, "missing argument for option \"",
+                                 option, "\"."));
+        return false;
+      }
     } else {
       status.SetError(
         cmStrCat(messageHint, "option \"", option, "\" is unknown."));
@@ -821,6 +849,17 @@
     return true;
   }
 
+  if (hasComparator) {
+    try {
+      status.GetMakefile().AddDefinition(
+        listName, list->sort(sortConfig, status.GetMakefile()).to_string());
+      return true;
+    } catch (std::invalid_argument& e) {
+      status.SetError(e.what());
+      return false;
+    }
+  }
+
   status.GetMakefile().AddDefinition(listName,
                                      list->sort(sortConfig).to_string());
   return true;
diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx
index 09026ca..c046e11 100644
--- a/Source/cmMakefileLibraryTargetGenerator.cxx
+++ b/Source/cmMakefileLibraryTargetGenerator.cxx
@@ -755,9 +755,13 @@
     // Construct object file lists that may be needed to expand the
     // rule.
     std::string buildObjs;
+    cmMakefileTargetGenerator::ResponseFlagFor responseMode =
+      this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY
+      ? cmMakefileTargetGenerator::ResponseFlagFor::Archive
+      : cmMakefileTargetGenerator::ResponseFlagFor::Link;
     this->CreateObjectLists(useLinkScript, useArchiveRules,
                             useResponseFileForObjects, buildObjs, depends,
-                            useWatcomQuote, linkLanguage);
+                            useWatcomQuote, linkLanguage, responseMode);
     if (!this->DeviceLinkObject.empty()) {
       buildObjs += " " +
         this->LocalGenerator->ConvertToOutputFormat(
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index 31300f4..59c0519 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -2260,9 +2260,9 @@
 
     // Create this response file.
     std::string const responseFileName =
-      (responseMode == Link) ? "linkLibs.rsp" : "deviceLinkLibs.rsp";
+      (responseMode == DeviceLink) ? "deviceLinkLibs.rsp" : "linkLibs.rsp";
     std::string const responseLang =
-      (responseMode == Link) ? linkLanguage : "CUDA";
+      (responseMode == DeviceLink) ? "CUDA" : linkLanguage;
     std::string link_rsp = this->CreateResponseFile(
       responseFileName, linkLibs, makefile_depends, responseLang);
 
@@ -2299,8 +2299,9 @@
     char const* sep = "";
     for (unsigned int i = 0; i < object_strings.size(); ++i) {
       // Number the response files.
-      std::string responseFileName = cmStrCat(
-        (responseMode == Link) ? "objects" : "deviceObjects", i + 1, ".rsp");
+      std::string responseFileName =
+        cmStrCat((responseMode == DeviceLink) ? "deviceObjects" : "objects",
+                 i + 1, ".rsp");
 
       // Create this response file.
       std::string objects_rsp = this->CreateResponseFile(
@@ -2423,10 +2424,17 @@
     responseFlagVar = cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_LINK_FLAG");
   } else if (mode == cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink) {
     responseFlagVar = "CMAKE_CUDA_RESPONSE_FILE_DEVICE_LINK_FLAG";
+  } else if (mode == cmMakefileTargetGenerator::ResponseFlagFor::Archive) {
+    responseFlagVar = cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_ARCHIVE_FLAG");
   }
 
   if (cmValue const p = this->Makefile->GetDefinition(responseFlagVar)) {
     responseFlag = *p;
+  } else if (mode == cmMakefileTargetGenerator::ResponseFlagFor::Archive) {
+    responseFlagVar = cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_LINK_FLAG");
+    if (cmValue const q = this->Makefile->GetDefinition(responseFlagVar)) {
+      responseFlag = *q;
+    }
   }
   return responseFlag;
 }
diff --git a/Source/cmMakefileTargetGenerator.h b/Source/cmMakefileTargetGenerator.h
index fe255f4..bcb5f26 100644
--- a/Source/cmMakefileTargetGenerator.h
+++ b/Source/cmMakefileTargetGenerator.h
@@ -174,8 +174,9 @@
 
   enum ResponseFlagFor
   {
+    Archive,
     Link,
-    DeviceLink
+    DeviceLink,
   };
 
   /** Create list of flags for link libraries. */
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
index 556d713..7ff9bb2 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -498,8 +498,15 @@
     }
 
     // build response file name
-    std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
-    cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
+    cmValue flag;
+    if (targetType == cmStateEnums::STATIC_LIBRARY) {
+      std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_ARCHIVE_FLAG";
+      flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
+    }
+    if (!flag) {
+      std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
+      flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
+    }
 
     if (flag) {
       responseFlag = *flag;
@@ -1590,9 +1597,15 @@
     cmStrCat("CMAKE_", this->TargetLinkLanguage(config));
 
   // build response file name
-  std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
-
-  cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
+  cmValue flag;
+  if (targetType == cmStateEnums::STATIC_LIBRARY) {
+    std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_ARCHIVE_FLAG";
+    flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
+  }
+  if (!flag) {
+    std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
+    flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
+  }
 
   bool const lang_supports_response =
     !(this->TargetLinkLanguage(config) == "RC" ||
diff --git a/Tests/FindPackageModeMakefileTest/CMakeLists.txt b/Tests/FindPackageModeMakefileTest/CMakeLists.txt
index 0b15be8..a9bcfc4 100644
--- a/Tests/FindPackageModeMakefileTest/CMakeLists.txt
+++ b/Tests/FindPackageModeMakefileTest/CMakeLists.txt
@@ -9,9 +9,10 @@
   execute_process(COMMAND ${CMAKE_MAKE_PROGRAM} -v
                   OUTPUT_VARIABLE makeVersionOutput
                   ERROR_QUIET
-                  TIMEOUT 10)
+                  TIMEOUT 10
+                  RESULT_VARIABLE _make_result)
   string(TOUPPER "${makeVersionOutput}" MAKE_VERSION_OUTPUT)
-  if("${MAKE_VERSION_OUTPUT}" MATCHES "GNU MAKE")
+  if(_make_result EQUAL 0 AND "${MAKE_VERSION_OUTPUT}" MATCHES "GNU MAKE")
 
     # build a library which we can search during the test
     add_library(foo STATIC foo.cpp)
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 6261947..8bf0af3 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -1231,8 +1231,9 @@
   if(UNIX AND NOT CYGWIN)
     execute_process(COMMAND ldd --help
       OUTPUT_VARIABLE LDD_HELP
-      ERROR_VARIABLE LDD_ERR)
-    if("${LDD_HELP}" MATCHES
+      ERROR_VARIABLE LDD_ERR
+      RESULT_VARIABLE LDD_RESULT)
+    if(LDD_RESULT EQUAL 0 AND "${LDD_HELP}" MATCHES
         "(-r, --function-relocs.*process data and function relocations.*-u, --unused.*print unused direct dependencies)")
       add_RunCMake_test(LinkWhatYouUse)
     endif()
diff --git a/Tests/RunCMake/list/RunCMakeTest.cmake b/Tests/RunCMake/list/RunCMakeTest.cmake
index 1fd86a9..9705b55 100644
--- a/Tests/RunCMake/list/RunCMakeTest.cmake
+++ b/Tests/RunCMake/list/RunCMakeTest.cmake
@@ -114,9 +114,18 @@
 run_cmake(SORT-DuplicateCompareOption)
 run_cmake(SORT-DuplicateCaseOption)
 run_cmake(SORT-NoCaseOption)
+run_cmake(SORT-COMPARATOR-UnknownFunction)
+run_cmake(SORT-COMPARATOR-NoOutput)
+run_cmake(SORT-COMPARATOR-CompareConflict)
+run_cmake(SORT-COMPARATOR-CompareConflictReverse)
+run_cmake(SORT-COMPARATOR-NoFunction)
+run_cmake(SORT-COMPARATOR-DuplicateOption)
+run_cmake(SORT-COMPARATOR-NotStrictWeak)
+run_cmake(SORT-COMPARATOR-NotStrictWeakLate)
 
 # Successful tests
 run_cmake(SORT)
+run_cmake(SORT-COMPARATOR)
 
 # argument tests
 run_cmake(PREPEND-NoArgs)
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflict-result.txt b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflict-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflict-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflict-stderr.txt b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflict-stderr.txt
new file mode 100644
index 0000000..0224d8c
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflict-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at SORT-COMPARATOR-CompareConflict\.cmake:10 \(list\):
+  list sub-command SORT option "COMPARATOR" is incompatible with "COMPARE"\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflict.cmake b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflict.cmake
new file mode 100644
index 0000000..2e62821
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflict.cmake
@@ -0,0 +1,10 @@
+function(my_cmp a b out)
+  if("${a}" STRLESS "${b}")
+    set(${out} TRUE PARENT_SCOPE)
+  else()
+    set(${out} FALSE PARENT_SCOPE)
+  endif()
+endfunction()
+
+set(mylist c a b)
+list(SORT mylist COMPARE STRING COMPARATOR my_cmp)
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflictReverse-result.txt b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflictReverse-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflictReverse-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflictReverse-stderr.txt b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflictReverse-stderr.txt
new file mode 100644
index 0000000..30659dc
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflictReverse-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at SORT-COMPARATOR-CompareConflictReverse\.cmake:10 \(list\):
+  list sub-command SORT option "COMPARE" is incompatible with "COMPARATOR"\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflictReverse.cmake b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflictReverse.cmake
new file mode 100644
index 0000000..c08314d
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-CompareConflictReverse.cmake
@@ -0,0 +1,10 @@
+function(my_cmp a b out)
+  if("${a}" STRLESS "${b}")
+    set(${out} TRUE PARENT_SCOPE)
+  else()
+    set(${out} FALSE PARENT_SCOPE)
+  endif()
+endfunction()
+
+set(mylist c a b)
+list(SORT mylist COMPARATOR my_cmp COMPARE STRING)
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-DuplicateOption-result.txt b/Tests/RunCMake/list/SORT-COMPARATOR-DuplicateOption-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-DuplicateOption-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-DuplicateOption-stderr.txt b/Tests/RunCMake/list/SORT-COMPARATOR-DuplicateOption-stderr.txt
new file mode 100644
index 0000000..b387e8e
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-DuplicateOption-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at SORT-COMPARATOR-DuplicateOption\.cmake:10 \(list\):
+  list sub-command SORT option "COMPARATOR" has been specified multiple
+  times\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-DuplicateOption.cmake b/Tests/RunCMake/list/SORT-COMPARATOR-DuplicateOption.cmake
new file mode 100644
index 0000000..418a843
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-DuplicateOption.cmake
@@ -0,0 +1,10 @@
+function(my_cmp a b out)
+  if("${a}" STRLESS "${b}")
+    set(${out} TRUE PARENT_SCOPE)
+  else()
+    set(${out} FALSE PARENT_SCOPE)
+  endif()
+endfunction()
+
+set(mylist c a b)
+list(SORT mylist COMPARATOR my_cmp COMPARATOR my_cmp)
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NoFunction-result.txt b/Tests/RunCMake/list/SORT-COMPARATOR-NoFunction-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-NoFunction-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NoFunction-stderr.txt b/Tests/RunCMake/list/SORT-COMPARATOR-NoFunction-stderr.txt
new file mode 100644
index 0000000..7b74e3b
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-NoFunction-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at SORT-COMPARATOR-NoFunction\.cmake:2 \(list\):
+  list sub-command SORT missing argument for option "COMPARATOR"\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NoFunction.cmake b/Tests/RunCMake/list/SORT-COMPARATOR-NoFunction.cmake
new file mode 100644
index 0000000..0c834f1
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-NoFunction.cmake
@@ -0,0 +1,2 @@
+set(mylist a b c)
+list(SORT mylist COMPARATOR)
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NoOutput-result.txt b/Tests/RunCMake/list/SORT-COMPARATOR-NoOutput-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-NoOutput-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NoOutput-stderr.txt b/Tests/RunCMake/list/SORT-COMPARATOR-NoOutput-stderr.txt
new file mode 100644
index 0000000..af5e3cc
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-NoOutput-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at SORT-COMPARATOR-NoOutput\.cmake:6 \(list\):
+  list sub-command SORT, COMPARATOR: function "bad_comparator" did not set
+  the output variable\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NoOutput.cmake b/Tests/RunCMake/list/SORT-COMPARATOR-NoOutput.cmake
new file mode 100644
index 0000000..9c279f8
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-NoOutput.cmake
@@ -0,0 +1,6 @@
+function(bad_comparator a b out)
+  # Deliberately does NOT set ${out}
+endfunction()
+
+set(mylist c a b)
+list(SORT mylist COMPARATOR bad_comparator)
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeak-result.txt b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeak-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeak-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeak-stderr.txt b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeak-stderr.txt
new file mode 100644
index 0000000..2978265
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeak-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at SORT-COMPARATOR-NotStrictWeak\.cmake:6 \(list\):
+  list sub-command SORT, COMPARATOR: function does not induce a strict weak
+  ordering\.  The comparator returned TRUE for both \(a, b\) and \(b, a\)\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeak.cmake b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeak.cmake
new file mode 100644
index 0000000..bf7e88f
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeak.cmake
@@ -0,0 +1,6 @@
+function(reflexive_comparator a b out)
+  set(${out} TRUE PARENT_SCOPE)
+endfunction()
+
+set(mylist c a b)
+list(SORT mylist COMPARATOR reflexive_comparator)
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeakLate-result.txt b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeakLate-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeakLate-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeakLate-stderr.txt b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeakLate-stderr.txt
new file mode 100644
index 0000000..6188b8d
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeakLate-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at SORT-COMPARATOR-NotStrictWeakLate\.cmake:19 \(list\):
+  list sub-command SORT, COMPARATOR: function does not induce a strict weak
+  ordering\.  The comparator returned TRUE for both \(a, b\) and \(b, a\)\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeakLate.cmake b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeakLate.cmake
new file mode 100644
index 0000000..c0193ed
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-NotStrictWeakLate.cmake
@@ -0,0 +1,19 @@
+# Comparator that is correct for most pairs but violates strict weak ordering
+# when both elements start with "b".
+function(late_violation a b result)
+  string(SUBSTRING "${a}" 0 1 a_first)
+  string(SUBSTRING "${b}" 0 1 b_first)
+  if(a_first STREQUAL "b" AND b_first STREQUAL "b")
+    # Violates asymmetry: always returns TRUE for b-vs-b pairs
+    set(${result} TRUE PARENT_SCOPE)
+  else()
+    if("${a}" STRLESS "${b}")
+      set(${result} TRUE PARENT_SCOPE)
+    else()
+      set(${result} FALSE PARENT_SCOPE)
+    endif()
+  endif()
+endfunction()
+
+set(mylist "a" "c" "bb" "ba")
+list(SORT mylist COMPARATOR late_violation)
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-UnknownFunction-result.txt b/Tests/RunCMake/list/SORT-COMPARATOR-UnknownFunction-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-UnknownFunction-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-UnknownFunction-stderr.txt b/Tests/RunCMake/list/SORT-COMPARATOR-UnknownFunction-stderr.txt
new file mode 100644
index 0000000..e5c9543
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-UnknownFunction-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at SORT-COMPARATOR-UnknownFunction\.cmake:2 \(list\):
+  list sub-command SORT, COMPARATOR: unknown function "no_such_function"\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR-UnknownFunction.cmake b/Tests/RunCMake/list/SORT-COMPARATOR-UnknownFunction.cmake
new file mode 100644
index 0000000..8e5901b
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR-UnknownFunction.cmake
@@ -0,0 +1,2 @@
+set(mylist a b c)
+list(SORT mylist COMPARATOR no_such_function)
diff --git a/Tests/RunCMake/list/SORT-COMPARATOR.cmake b/Tests/RunCMake/list/SORT-COMPARATOR.cmake
new file mode 100644
index 0000000..0303846
--- /dev/null
+++ b/Tests/RunCMake/list/SORT-COMPARATOR.cmake
@@ -0,0 +1,135 @@
+# Comparator: returns TRUE if a < b (string comparison)
+function(string_less a b result)
+  if("${a}" STRLESS "${b}")
+    set(${result} TRUE PARENT_SCOPE)
+  else()
+    set(${result} FALSE PARENT_SCOPE)
+  endif()
+endfunction()
+
+# Comparator as macro
+macro(string_less_macro a b result)
+  if(${a} STRLESS ${b})
+    set(${result} TRUE)
+  else()
+    set(${result} FALSE)
+  endif()
+endmacro()
+
+# Comparator: string length comparison
+function(shorter_first a b result)
+  string(LENGTH "${a}" len_a)
+  string(LENGTH "${b}" len_b)
+  if(len_a LESS len_b)
+    set(${result} TRUE PARENT_SCOPE)
+  else()
+    set(${result} FALSE PARENT_SCOPE)
+  endif()
+endfunction()
+
+## Basic COMPARATOR with function
+set(mylist c a b)
+list(SORT mylist COMPARATOR string_less)
+if(NOT mylist STREQUAL "a;b;c")
+  message(FATAL_ERROR "SORT(COMPARATOR function) is \"${mylist}\", expected \"a;b;c\"")
+endif()
+
+## COMPARATOR with macro
+set(mylist c a b)
+list(SORT mylist COMPARATOR string_less_macro)
+if(NOT mylist STREQUAL "a;b;c")
+  message(FATAL_ERROR "SORT(COMPARATOR macro) is \"${mylist}\", expected \"a;b;c\"")
+endif()
+
+## COMPARATOR with ORDER DESCENDING
+set(mylist c a b)
+list(SORT mylist COMPARATOR string_less ORDER DESCENDING)
+if(NOT mylist STREQUAL "c;b;a")
+  message(FATAL_ERROR "SORT(COMPARATOR ORDER DESCENDING) is \"${mylist}\", expected \"c;b;a\"")
+endif()
+
+## COMPARATOR with CASE INSENSITIVE
+# With CASE INSENSITIVE, values are lowercased before being passed to the comparator
+set(mylist "C" "a" "B")
+list(SORT mylist COMPARATOR string_less CASE INSENSITIVE)
+if(NOT mylist STREQUAL "a;B;C")
+  message(FATAL_ERROR "SORT(COMPARATOR CASE INSENSITIVE) is \"${mylist}\", expected \"a;B;C\"")
+endif()
+
+## COMPARATOR with CASE INSENSITIVE and ORDER DESCENDING
+set(mylist "C" "a" "B")
+list(SORT mylist COMPARATOR string_less CASE INSENSITIVE ORDER DESCENDING)
+if(NOT mylist STREQUAL "C;B;a")
+  message(FATAL_ERROR "SORT(COMPARATOR CASE INSENSITIVE ORDER DESCENDING) is \"${mylist}\", expected \"C;B;a\"")
+endif()
+
+## Custom comparator (sort by string length)
+set(mylist "bbb" "a" "cc")
+list(SORT mylist COMPARATOR shorter_first)
+if(NOT mylist STREQUAL "a;cc;bbb")
+  message(FATAL_ERROR "SORT(COMPARATOR shorter_first) is \"${mylist}\", expected \"a;cc;bbb\"")
+endif()
+
+## Custom comparator with ORDER DESCENDING (sort by length descending)
+set(mylist "bbb" "a" "cc")
+list(SORT mylist COMPARATOR shorter_first ORDER DESCENDING)
+if(NOT mylist STREQUAL "bbb;cc;a")
+  message(FATAL_ERROR "SORT(COMPARATOR shorter_first ORDER DESCENDING) is \"${mylist}\", expected \"bbb;cc;a\"")
+endif()
+
+## Empty list (no-op)
+set(empty_list "")
+list(SORT empty_list COMPARATOR string_less)
+if(NOT empty_list STREQUAL "")
+  message(FATAL_ERROR "SORT(COMPARATOR empty) is \"${empty_list}\", expected \"\"")
+endif()
+
+## Single element list (no-op)
+set(mylist "only")
+list(SORT mylist COMPARATOR string_less)
+if(NOT mylist STREQUAL "only")
+  message(FATAL_ERROR "SORT(COMPARATOR single) is \"${mylist}\", expected \"only\"")
+endif()
+
+## Already sorted list
+set(mylist a b c)
+list(SORT mylist COMPARATOR string_less)
+if(NOT mylist STREQUAL "a;b;c")
+  message(FATAL_ERROR "SORT(COMPARATOR already-sorted) is \"${mylist}\", expected \"a;b;c\"")
+endif()
+
+## Recursive COMPARATOR - verify inner and outer output variables are distinct
+# Inner comparator: checks that neither element equals its own output variable
+# name (which would mean the outer and inner names collided), then performs a
+# valid string comparison to satisfy strict weak ordering.
+function(check_outer_name a b out)
+  if("${a}" STREQUAL "${out}")
+    message(FATAL_ERROR "Recursive COMPARATOR: inner and outer output variable names are the same: ${out}")
+  endif()
+  if("${b}" STREQUAL "${out}")
+    message(FATAL_ERROR "Recursive COMPARATOR: inner and outer output variable names are the same: ${out}")
+  endif()
+  if("${a}" STRLESS "${b}")
+    set(${out} TRUE PARENT_SCOPE)
+  else()
+    set(${out} FALSE PARENT_SCOPE)
+  endif()
+endfunction()
+
+# Outer comparator: passes its own output variable name into a nested sort
+# as a list element, so the inner comparator can compare the two names.
+function(nested_comparator a b out)
+  set(_inner "${out}" dummy)
+  list(SORT _inner COMPARATOR check_outer_name)
+  if("${a}" STRLESS "${b}")
+    set(${out} TRUE PARENT_SCOPE)
+  else()
+    set(${out} FALSE PARENT_SCOPE)
+  endif()
+endfunction()
+
+set(mylist c a b)
+list(SORT mylist COMPARATOR nested_comparator)
+if(NOT mylist STREQUAL "a;b;c")
+  message(FATAL_ERROR "SORT(COMPARATOR nested) is \"${mylist}\", expected \"a;b;c\"")
+endif()
diff --git a/Utilities/Sphinx/conf.py.in b/Utilities/Sphinx/conf.py.in
index 7cceaf2..67450e6 100644
--- a/Utilities/Sphinx/conf.py.in
+++ b/Utilities/Sphinx/conf.py.in
@@ -100,6 +100,7 @@
     r'about:',
     r'https://gitlab\.kitware\.com/cmake/community/-/wikis/doc/cpack',
     r'https://web\.archive\.org/',
+    r'https://en\.cppreference\.com/',
     r'https://www\.freedesktop\.org/',
     r'https://www\.gnu\.org/',
     r'https://www\.intel\.com/',