Merge topic 'clang-tidy-module-pragma-once-check'

91a7c0b51c CMake: fix violations of #pragma once check
aa0c99c55c clang-tidy module: add test for #pragma once check
c9af6f2ff6 clang-tidy module: add #pragma once check
68a4d97da7 clang-tidy module: allow header files in test cases

Acked-by: Kitware Robot <kwrobot@kitware.com>
Acked-by: buildbot <buildbot@kitware.com>
Acked-by: Ben Boeckel <ben.boeckel@kitware.com>
Merge-request: !7933
diff --git a/.clang-tidy b/.clang-tidy
index 7ee8bf0..9ce5f42 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,24 +1,31 @@
 ---
 Checks: "-*,\
 bugprone-*,\
+-bugprone-assignment-in-if-condition,\
 -bugprone-easily-swappable-parameters,\
 -bugprone-implicit-widening-of-multiplication-result,\
 -bugprone-macro-parentheses,\
 -bugprone-misplaced-widening-cast,\
 -bugprone-narrowing-conversions,\
 -bugprone-too-small-loop-variable,\
+-bugprone-unchecked-optional-access,\
 misc-*,\
+-misc-confusable-identifiers,\
+-misc-const-correctness,\
 -misc-no-recursion,\
 -misc-non-private-member-variables-in-classes,\
 -misc-static-assert,\
 modernize-*,\
 -modernize-avoid-c-arrays,\
+-modernize-macro-to-enum,\
 -modernize-return-braced-init-list,\
+-modernize-use-emplace,\
 -modernize-use-nodiscard,\
 -modernize-use-noexcept,\
 -modernize-use-trailing-return-type,\
 -modernize-use-transparent-functors,\
 performance-*,\
+-performance-inefficient-vector-operation,\
 readability-*,\
 -readability-convert-member-functions-to-static,\
 -readability-function-cognitive-complexity,\
@@ -28,9 +35,11 @@
 -readability-implicit-bool-conversion,\
 -readability-inconsistent-declaration-parameter-name,\
 -readability-magic-numbers,\
+-readability-make-member-function-const,\
 -readability-named-parameter,\
 -readability-redundant-declaration,\
 -readability-redundant-member-init,\
+-readability-simplify-boolean-expr,\
 -readability-suspicious-call-argument,\
 -readability-uppercase-literal-suffix,\
 cmake-*,\
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 707756e..4c44b07 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -57,7 +57,7 @@
 
 p:doc-package:
     extends:
-        - .fedora36_sphinx_package
+        - .fedora37_sphinx_package
         - .cmake_prep_doc_linux
         - .linux_builder_tags_qt
         - .cmake_doc_artifacts
@@ -105,16 +105,16 @@
         - .linux_builder_tags
         - .run_automatically
 
-l:tidy-fedora36:
+l:tidy-fedora37:
     extends:
-        - .fedora36_tidy
+        - .fedora37_tidy
         - .cmake_build_linux
         - .linux_builder_tags_qt
         - .run_automatically
 
-l:sphinx-fedora36:
+l:sphinx-fedora37:
     extends:
-        - .fedora36_sphinx
+        - .fedora37_sphinx
         - .cmake_build_linux
         - .linux_builder_tags_qt
         - .run_automatically
@@ -122,9 +122,9 @@
         CMAKE_CI_JOB_CONTINUOUS: "true"
         CMAKE_CI_JOB_HELP: "true"
 
-l:clang-analyzer-fedora36:
+l:clang-analyzer-fedora37:
     extends:
-        - .fedora36_clang_analyzer
+        - .fedora37_clang_analyzer
         - .cmake_build_linux
         - .linux_builder_tags_qt
         - .run_automatically
@@ -181,7 +181,7 @@
         - .debian10_ninja_clang
         - .cmake_test_linux_release
         - .linux_builder_tags_qt
-        - .run_manually
+        - .run_dependent
         - .needs_centos6_x86_64
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
@@ -191,22 +191,22 @@
         - .debian10_makefiles_clang
         - .cmake_test_linux_release
         - .linux_builder_tags_qt
-        - .run_manually
+        - .run_dependent
         - .needs_centos6_x86_64
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:fedora36-makefiles:
+t:fedora37-makefiles:
     extends:
-        - .fedora36_makefiles
+        - .fedora37_makefiles
         - .cmake_test_linux_release
         - .linux_builder_tags_qt
         - .run_dependent
         - .needs_centos6_x86_64
 
-t:fedora36-makefiles-nospace:
+t:fedora37-makefiles-nospace:
     extends:
-        - .fedora36_makefiles
+        - .fedora37_makefiles
         - .cmake_test_linux_release
         - .linux_builder_tags_qt
         - .cmake_junit_artifacts
@@ -214,7 +214,7 @@
         - .needs_centos6_x86_64
     variables:
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake-ci"
-        CMAKE_CI_BUILD_NAME: fedora36_makefiles_nospace
+        CMAKE_CI_BUILD_NAME: fedora37_makefiles_nospace
         CMAKE_CI_JOB_NIGHTLY: "true"
 
 t:cuda9.2-nvidia:
@@ -295,9 +295,9 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-b:fedora36-ninja:
+b:fedora37-ninja:
     extends:
-        - .fedora36_ninja
+        - .fedora37_ninja
         - .cmake_build_linux
         - .cmake_build_artifacts
         - .linux_builder_tags_qt
@@ -332,40 +332,40 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-b:fedora36-extdeps:
+b:fedora37-extdeps:
     extends:
-        - .fedora36_extdeps
+        - .fedora37_extdeps
         - .cmake_build_linux_standalone
         - .linux_builder_tags
         - .run_manually
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:fedora36-ninja:
+t:fedora37-ninja:
     extends:
-        - .fedora36_ninja
+        - .fedora37_ninja
         - .cmake_test_linux
         - .linux_builder_tags_x11
         - .cmake_test_artifacts
         - .run_dependent
     dependencies:
-        - b:fedora36-ninja
+        - b:fedora37-ninja
     needs:
-        - b:fedora36-ninja
+        - b:fedora37-ninja
     variables:
         CMAKE_CI_JOB_CONTINUOUS: "true"
 
-t:fedora36-ninja-multi:
+t:fedora37-ninja-multi:
     extends:
-        - .fedora36_ninja_multi
+        - .fedora37_ninja_multi
         - .cmake_test_linux_external
         - .linux_builder_tags_qt
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
-        - t:fedora36-ninja
+        - t:fedora37-ninja
     needs:
-        - t:fedora36-ninja
+        - t:fedora37-ninja
 
 t:intel2016-makefiles:
     extends:
@@ -704,9 +704,9 @@
 
 ## Sanitizer builds
 
-b:fedora36-asan:
+b:fedora37-asan:
     extends:
-        - .fedora36_asan
+        - .fedora37_asan
         - .cmake_build_linux
         - .cmake_build_artifacts
         - .linux_builder_tags_qt
@@ -714,16 +714,16 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:fedora36-asan:
+t:fedora37-asan:
     extends:
-        - .fedora36_asan
+        - .fedora37_asan
         - .cmake_memcheck_linux
         - .linux_builder_tags_qt
         - .run_dependent
     dependencies:
-        - b:fedora36-asan
+        - b:fedora37-asan
     needs:
-        - b:fedora36-asan
+        - b:fedora37-asan
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
diff --git a/.gitlab/ci/configure_fedora36_clang_analyzer.cmake b/.gitlab/ci/configure_fedora36_clang_analyzer.cmake
deleted file mode 100644
index 456936b..0000000
--- a/.gitlab/ci/configure_fedora36_clang_analyzer.cmake
+++ /dev/null
@@ -1 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora36_common.cmake")
diff --git a/.gitlab/ci/configure_fedora36_asan.cmake b/.gitlab/ci/configure_fedora37_asan.cmake
similarity index 65%
rename from .gitlab/ci/configure_fedora36_asan.cmake
rename to .gitlab/ci/configure_fedora37_asan.cmake
index 51977d9..363e953 100644
--- a/.gitlab/ci/configure_fedora36_asan.cmake
+++ b/.gitlab/ci/configure_fedora37_asan.cmake
@@ -1,4 +1,4 @@
 set(CMAKE_C_FLAGS "-fsanitize=address" CACHE STRING "")
 set(CMAKE_CXX_FLAGS "-fsanitize=address" CACHE STRING "")
 
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora36_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora37_common.cmake")
diff --git a/.gitlab/ci/configure_fedora37_clang_analyzer.cmake b/.gitlab/ci/configure_fedora37_clang_analyzer.cmake
new file mode 100644
index 0000000..f4c4cdd
--- /dev/null
+++ b/.gitlab/ci/configure_fedora37_clang_analyzer.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora37_common.cmake")
diff --git a/.gitlab/ci/configure_fedora36_common.cmake b/.gitlab/ci/configure_fedora37_common.cmake
similarity index 100%
rename from .gitlab/ci/configure_fedora36_common.cmake
rename to .gitlab/ci/configure_fedora37_common.cmake
diff --git a/.gitlab/ci/configure_fedora36_extdeps.cmake b/.gitlab/ci/configure_fedora37_extdeps.cmake
similarity index 100%
rename from .gitlab/ci/configure_fedora36_extdeps.cmake
rename to .gitlab/ci/configure_fedora37_extdeps.cmake
diff --git a/.gitlab/ci/configure_fedora36_makefiles.cmake b/.gitlab/ci/configure_fedora37_makefiles.cmake
similarity index 100%
rename from .gitlab/ci/configure_fedora36_makefiles.cmake
rename to .gitlab/ci/configure_fedora37_makefiles.cmake
diff --git a/.gitlab/ci/configure_fedora36_ninja.cmake b/.gitlab/ci/configure_fedora37_ninja.cmake
similarity index 86%
rename from .gitlab/ci/configure_fedora36_ninja.cmake
rename to .gitlab/ci/configure_fedora37_ninja.cmake
index 45d9192..3defa5a 100644
--- a/.gitlab/ci/configure_fedora36_ninja.cmake
+++ b/.gitlab/ci/configure_fedora37_ninja.cmake
@@ -10,4 +10,4 @@
 # Cover compilation with C++11 only and not higher standards.
 set(CMAKE_CXX_STANDARD "11" CACHE STRING "")
 
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora36_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora37_common.cmake")
diff --git a/.gitlab/ci/configure_fedora36_ninja_multi.cmake b/.gitlab/ci/configure_fedora37_ninja_multi.cmake
similarity index 100%
rename from .gitlab/ci/configure_fedora36_ninja_multi.cmake
rename to .gitlab/ci/configure_fedora37_ninja_multi.cmake
diff --git a/.gitlab/ci/configure_fedora36_sphinx.cmake b/.gitlab/ci/configure_fedora37_sphinx.cmake
similarity index 100%
rename from .gitlab/ci/configure_fedora36_sphinx.cmake
rename to .gitlab/ci/configure_fedora37_sphinx.cmake
diff --git a/.gitlab/ci/configure_fedora36_sphinx_package.cmake b/.gitlab/ci/configure_fedora37_sphinx_package.cmake
similarity index 100%
rename from .gitlab/ci/configure_fedora36_sphinx_package.cmake
rename to .gitlab/ci/configure_fedora37_sphinx_package.cmake
diff --git a/.gitlab/ci/configure_fedora36_tidy.cmake b/.gitlab/ci/configure_fedora37_tidy.cmake
similarity index 78%
rename from .gitlab/ci/configure_fedora36_tidy.cmake
rename to .gitlab/ci/configure_fedora37_tidy.cmake
index 2d0eeeb..f8eb9ab 100644
--- a/.gitlab/ci/configure_fedora36_tidy.cmake
+++ b/.gitlab/ci/configure_fedora37_tidy.cmake
@@ -2,4 +2,4 @@
 set(CMake_USE_CLANG_TIDY_MODULE ON CACHE BOOL "")
 set(CMake_CLANG_TIDY_MODULE "$ENV{CI_PROJECT_DIR}/Utilities/ClangTidyModule/build/libcmake-clang-tidy-module.so" CACHE FILEPATH "")
 
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora36_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora37_common.cmake")
diff --git a/.gitlab/ci/ctest_memcheck_fedora36_asan.lsan.supp b/.gitlab/ci/ctest_memcheck_fedora37_asan.lsan.supp
similarity index 100%
rename from .gitlab/ci/ctest_memcheck_fedora36_asan.lsan.supp
rename to .gitlab/ci/ctest_memcheck_fedora37_asan.lsan.supp
diff --git a/.gitlab/ci/docker/fedora36/Dockerfile b/.gitlab/ci/docker/fedora37/Dockerfile
similarity index 89%
rename from .gitlab/ci/docker/fedora36/Dockerfile
rename to .gitlab/ci/docker/fedora37/Dockerfile
index ea42561..13ef9aa 100644
--- a/.gitlab/ci/docker/fedora36/Dockerfile
+++ b/.gitlab/ci/docker/fedora37/Dockerfile
@@ -1,22 +1,22 @@
-FROM fedora:36 as rvm-build
+FROM fedora:37 as rvm-build
 MAINTAINER Ben Boeckel <ben.boeckel@kitware.com>
 
 COPY install_rvm.sh /root/install_rvm.sh
 RUN sh /root/install_rvm.sh
 
-FROM fedora:36 AS clang-tidy-headers
+FROM fedora:37 AS clang-tidy-headers
 MAINTAINER Kyle Edwards <kyle.edwards@kitware.com>
 
 COPY install_clang_tidy_headers.sh /root/install_clang_tidy_headers.sh
 RUN sh /root/install_clang_tidy_headers.sh
 
-FROM fedora:36 AS iwyu
+FROM fedora:37 AS iwyu
 MAINTAINER Kyle Edwards <kyle.edwards@kitware.com>
 
 COPY install_iwyu.sh /root/install_iwyu.sh
 RUN sh /root/install_iwyu.sh
 
-FROM fedora:36
+FROM fedora:37
 MAINTAINER Ben Boeckel <ben.boeckel@kitware.com>
 
 COPY install_deps.sh /root/install_deps.sh
diff --git a/.gitlab/ci/docker/fedora36/install_clang_tidy_headers.sh b/.gitlab/ci/docker/fedora37/install_clang_tidy_headers.sh
similarity index 100%
rename from .gitlab/ci/docker/fedora36/install_clang_tidy_headers.sh
rename to .gitlab/ci/docker/fedora37/install_clang_tidy_headers.sh
diff --git a/.gitlab/ci/docker/fedora36/install_deps.sh b/.gitlab/ci/docker/fedora37/install_deps.sh
similarity index 98%
rename from .gitlab/ci/docker/fedora36/install_deps.sh
rename to .gitlab/ci/docker/fedora37/install_deps.sh
index f117888..afb8560 100755
--- a/.gitlab/ci/docker/fedora36/install_deps.sh
+++ b/.gitlab/ci/docker/fedora37/install_deps.sh
@@ -11,8 +11,11 @@
 
 # Install development tools.
 dnf install --setopt=install_weak_deps=False -y \
+    clang \
     clang-tools-extra \
     compiler-rt \
+    flang \
+    flang-devel \
     gcc-c++ \
     git-core \
     make
diff --git a/.gitlab/ci/docker/fedora36/install_iwyu.sh b/.gitlab/ci/docker/fedora37/install_iwyu.sh
similarity index 100%
rename from .gitlab/ci/docker/fedora36/install_iwyu.sh
rename to .gitlab/ci/docker/fedora37/install_iwyu.sh
diff --git a/.gitlab/ci/docker/fedora36/install_rvm.sh b/.gitlab/ci/docker/fedora37/install_rvm.sh
similarity index 100%
rename from .gitlab/ci/docker/fedora36/install_rvm.sh
rename to .gitlab/ci/docker/fedora37/install_rvm.sh
diff --git a/.gitlab/ci/env_fedora36_asan.sh b/.gitlab/ci/env_fedora37_asan.sh
similarity index 100%
rename from .gitlab/ci/env_fedora36_asan.sh
rename to .gitlab/ci/env_fedora37_asan.sh
diff --git a/.gitlab/ci/env_fedora36_clang_analyzer.sh b/.gitlab/ci/env_fedora37_clang_analyzer.sh
similarity index 100%
rename from .gitlab/ci/env_fedora36_clang_analyzer.sh
rename to .gitlab/ci/env_fedora37_clang_analyzer.sh
diff --git a/.gitlab/ci/env_fedora36_extdeps.sh b/.gitlab/ci/env_fedora37_extdeps.sh
similarity index 100%
rename from .gitlab/ci/env_fedora36_extdeps.sh
rename to .gitlab/ci/env_fedora37_extdeps.sh
diff --git a/.gitlab/ci/env_fedora36_makefiles.cmake b/.gitlab/ci/env_fedora37_makefiles.cmake
similarity index 100%
rename from .gitlab/ci/env_fedora36_makefiles.cmake
rename to .gitlab/ci/env_fedora37_makefiles.cmake
diff --git a/.gitlab/ci/env_fedora36_makefiles.sh b/.gitlab/ci/env_fedora37_makefiles.sh
similarity index 100%
rename from .gitlab/ci/env_fedora36_makefiles.sh
rename to .gitlab/ci/env_fedora37_makefiles.sh
diff --git a/.gitlab/ci/env_fedora36_ninja.sh b/.gitlab/ci/env_fedora37_ninja.sh
similarity index 100%
rename from .gitlab/ci/env_fedora36_ninja.sh
rename to .gitlab/ci/env_fedora37_ninja.sh
diff --git a/.gitlab/ci/env_fedora36_ninja_multi.sh b/.gitlab/ci/env_fedora37_ninja_multi.sh
similarity index 100%
rename from .gitlab/ci/env_fedora36_ninja_multi.sh
rename to .gitlab/ci/env_fedora37_ninja_multi.sh
diff --git a/.gitlab/ci/env_fedora36_tidy.sh b/.gitlab/ci/env_fedora37_tidy.sh
similarity index 100%
rename from .gitlab/ci/env_fedora36_tidy.sh
rename to .gitlab/ci/env_fedora37_tidy.sh
diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml
index 84950d2..6a99f33 100644
--- a/.gitlab/os-linux.yml
+++ b/.gitlab/os-linux.yml
@@ -5,7 +5,7 @@
 ### Release
 
 .linux_prep_source:
-    image: "fedora:36"
+    image: "fedora:37"
 
     variables:
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
@@ -68,8 +68,8 @@
 
 ### Fedora
 
-.fedora36:
-    image: "kitware/cmake:ci-fedora36-x86_64-2022-10-04"
+.fedora37:
+    image: "kitware/cmake:ci-fedora37-x86_64-2022-11-16"
 
     variables:
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci/long file name for testing purposes"
@@ -77,37 +77,37 @@
 
 #### Lint builds
 
-.fedora36_tidy:
-    extends: .fedora36
+.fedora37_tidy:
+    extends: .fedora37
 
     variables:
-        CMAKE_CONFIGURATION: fedora36_tidy
+        CMAKE_CONFIGURATION: fedora37_tidy
         CTEST_NO_WARNINGS_ALLOWED: 1
         CMAKE_CI_NO_INSTALL: 1
 
-.fedora36_clang_analyzer:
-    extends: .fedora36
+.fedora37_clang_analyzer:
+    extends: .fedora37
 
     variables:
-        CMAKE_CONFIGURATION: fedora36_clang_analyzer
+        CMAKE_CONFIGURATION: fedora37_clang_analyzer
         CMAKE_CI_BUILD_TYPE: Debug
         CTEST_NO_WARNINGS_ALLOWED: 1
         CMAKE_CI_NO_INSTALL: 1
 
-.fedora36_sphinx:
-    extends: .fedora36
+.fedora37_sphinx:
+    extends: .fedora37
 
     variables:
-        CMAKE_CONFIGURATION: fedora36_sphinx
+        CMAKE_CONFIGURATION: fedora37_sphinx
         CTEST_NO_WARNINGS_ALLOWED: 1
         CTEST_SOURCE_SUBDIRECTORY: "Utilities/Sphinx"
         CMAKE_CI_NO_INSTALL: 1
 
-.fedora36_sphinx_package:
-    extends: .fedora36
+.fedora37_sphinx_package:
+    extends: .fedora37
 
     variables:
-        CMAKE_CONFIGURATION: fedora36_sphinx_package
+        CMAKE_CONFIGURATION: fedora37_sphinx_package
         CTEST_SOURCE_SUBDIRECTORY: "Utilities/Sphinx"
 
 #### Build and test
@@ -153,35 +153,35 @@
         CMAKE_CI_BUILD_TYPE: Release
         CTEST_NO_WARNINGS_ALLOWED: 1
 
-.fedora36_extdeps:
-    extends: .fedora36
+.fedora37_extdeps:
+    extends: .fedora37
 
     variables:
-        CMAKE_CONFIGURATION: fedora36_extdeps
+        CMAKE_CONFIGURATION: fedora37_extdeps
         CMAKE_CI_BUILD_TYPE: Release
         CTEST_NO_WARNINGS_ALLOWED: 1
 
-.fedora36_ninja:
-    extends: .fedora36
+.fedora37_ninja:
+    extends: .fedora37
 
     variables:
-        CMAKE_CONFIGURATION: fedora36_ninja
+        CMAKE_CONFIGURATION: fedora37_ninja
         CMAKE_CI_BUILD_TYPE: Release
         CTEST_NO_WARNINGS_ALLOWED: 1
 
-.fedora36_ninja_multi:
-    extends: .fedora36
+.fedora37_ninja_multi:
+    extends: .fedora37
 
     variables:
-        CMAKE_CONFIGURATION: fedora36_ninja_multi
+        CMAKE_CONFIGURATION: fedora37_ninja_multi
         CTEST_NO_WARNINGS_ALLOWED: 1
         CMAKE_GENERATOR: "Ninja Multi-Config"
 
-.fedora36_makefiles:
-    extends: .fedora36
+.fedora37_makefiles:
+    extends: .fedora37
 
     variables:
-        CMAKE_CONFIGURATION: fedora36_makefiles
+        CMAKE_CONFIGURATION: fedora37_makefiles
         CTEST_NO_WARNINGS_ALLOWED: 1
         CMAKE_GENERATOR: "Unix Makefiles"
 
@@ -213,13 +213,13 @@
         CTEST_MEMORYCHECK_TYPE: AddressSanitizer
         CTEST_MEMORYCHECK_SANITIZER_OPTIONS: ""
 
-.fedora36_asan:
+.fedora37_asan:
     extends:
-        - .fedora36
+        - .fedora37
         - .fedora_asan_addon
 
     variables:
-        CMAKE_CONFIGURATION: fedora36_asan
+        CMAKE_CONFIGURATION: fedora37_asan
 
 ### Intel Compiler
 
@@ -437,7 +437,7 @@
 
 .cmake_codespell_linux:
     stage: build
-    extends: .fedora36
+    extends: .fedora37
     script:
         - codespell
     interruptible: true
@@ -580,7 +580,7 @@
 .cmake_org_help:
     stage: build
     extends:
-        - .fedora36
+        - .fedora37
         - .linux_builder_tags
         - .cmake_org_help_artifacts
     script:
diff --git a/.gitlab/upload.yml b/.gitlab/upload.yml
index 38d40a9..27a6ab7 100644
--- a/.gitlab/upload.yml
+++ b/.gitlab/upload.yml
@@ -1,7 +1,7 @@
 # Steps for uploading artifacts
 
 .rsync_upload_package:
-    image: "fedora:36"
+    image: "fedora:37"
     stage: upload
     tags:
         - cmake
@@ -21,7 +21,7 @@
 
 .rsync_upload_help:
     stage: upload
-    image: "fedora:36"
+    image: "fedora:37"
     tags:
         - cmake
         - docker
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index faa793f..a72eac1 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -1401,6 +1401,13 @@
   Note that ``tgt`` is not added as a dependency of the target this
   expression is evaluated on.
 
+  .. versionchanged:: 3.26
+    When encountered during evaluation of :ref:`Target Usage Requirements`,
+    typically in an ``INTERFACE_*`` target property, lookup of the ``tgt``
+    name occurs in the directory of the target specifying the requirement,
+    rather than the directory of the consuming target for which the
+    expression is being evaluated.
+
 .. genex:: $<TARGET_PROPERTY:prop>
 
   Value of the property ``prop`` on the target for which the expression
@@ -1704,6 +1711,13 @@
   when the target is used by another target in the same buildsystem. Expands to
   the empty string otherwise.
 
+.. genex:: $<BUILD_LOCAL_INTERFACE:...>
+
+  .. versionadded:: 3.26
+
+  Content of ``...`` when the target is used by another target in the same
+  buildsystem. Expands to the empty string otherwise.
+
 .. genex:: $<INSTALL_PREFIX>
 
   Content of the install prefix when the target is exported via
diff --git a/Help/release/dev/build-local-interface-genex.rst b/Help/release/dev/build-local-interface-genex.rst
new file mode 100644
index 0000000..7fe702e
--- /dev/null
+++ b/Help/release/dev/build-local-interface-genex.rst
@@ -0,0 +1,5 @@
+build-local-interface-genex
+---------------------------
+
+* The :genex:`BUILD_LOCAL_INTERFACE` generator expression was added to
+  prevent usage requirements from being exported to dependent projects.
diff --git a/Modules/CMakeDetermineFortranCompiler.cmake b/Modules/CMakeDetermineFortranCompiler.cmake
index 087c0f6..d169012 100644
--- a/Modules/CMakeDetermineFortranCompiler.cmake
+++ b/Modules/CMakeDetermineFortranCompiler.cmake
@@ -284,6 +284,9 @@
   set(SET_MSVC_Fortran_ARCHITECTURE_ID
     "set(MSVC_Fortran_ARCHITECTURE_ID ${MSVC_Fortran_ARCHITECTURE_ID})")
 endif()
+if(CMAKE_Fortran_COMPILER_ID STREQUAL "NVHPC")
+  set(CMAKE_Fortran_VENDOR_SOURCE_FILE_EXTENSIONS ";cuf;CUF")
+endif()
 # configure variables set in this file for fast reload later on
 configure_file(${CMAKE_ROOT}/Modules/CMakeFortranCompiler.cmake.in
   ${CMAKE_PLATFORM_INFO_DIR}/CMakeFortranCompiler.cmake
diff --git a/Modules/CMakeFortranCompiler.cmake.in b/Modules/CMakeFortranCompiler.cmake.in
index 97f891e..f52ad02 100644
--- a/Modules/CMakeFortranCompiler.cmake.in
+++ b/Modules/CMakeFortranCompiler.cmake.in
@@ -25,7 +25,7 @@
 set(CMAKE_Fortran_COMPILER_SUPPORTS_F90 @CMAKE_Fortran_COMPILER_SUPPORTS_F90@)
 
 set(CMAKE_Fortran_COMPILER_ID_RUN 1)
-set(CMAKE_Fortran_SOURCE_FILE_EXTENSIONS f;F;fpp;FPP;f77;F77;f90;F90;for;For;FOR;f95;F95)
+set(CMAKE_Fortran_SOURCE_FILE_EXTENSIONS f;F;fpp;FPP;f77;F77;f90;F90;for;For;FOR;f95;F95@CMAKE_Fortran_VENDOR_SOURCE_FILE_EXTENSIONS@)
 set(CMAKE_Fortran_IGNORE_EXTENSIONS h;H;o;O;obj;OBJ;def;DEF;rc;RC)
 set(CMAKE_Fortran_LINKER_PREFERENCE 20)
 if(UNIX)
diff --git a/Modules/FindCUDAToolkit.cmake b/Modules/FindCUDAToolkit.cmake
index 365b72a..62e54c8 100644
--- a/Modules/FindCUDAToolkit.cmake
+++ b/Modules/FindCUDAToolkit.cmake
@@ -1060,7 +1060,9 @@
   if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 11.1.0)
     if(NOT TARGET CUDA::nvptxcompiler_static)
       _CUDAToolkit_find_and_add_import_lib(nvptxcompiler_static DEPS cuda_driver)
-      target_link_libraries(CUDA::nvptxcompiler_static INTERFACE Threads::Threads)
+      if(TARGET CUDA::nvptxcompiler_static)
+        target_link_libraries(CUDA::nvptxcompiler_static INTERFACE Threads::Threads)
+      endif()
     endif()
   endif()
 
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index c268a92..c0709c6 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -196,7 +196,6 @@
   cmDependsCompiler.h
   cmDocumentation.cxx
   cmDocumentationFormatter.cxx
-  cmDocumentationSection.cxx
   cmDynamicLoader.cxx
   cmDynamicLoader.h
   cmELF.h
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 20d3508..ee7639f 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 3)
 set(CMake_VERSION_MINOR 25)
-set(CMake_VERSION_PATCH 20221117)
+set(CMake_VERSION_PATCH 20221118)
 #set(CMake_VERSION_RC 0)
 set(CMake_VERSION_IS_DIRTY 0)
 
diff --git a/Source/CPack/cpack.cxx b/Source/CPack/cpack.cxx
index f06946b..c228f07 100644
--- a/Source/CPack/cpack.cxx
+++ b/Source/CPack/cpack.cxx
@@ -5,10 +5,12 @@
 #include <cstddef>
 #include <functional>
 #include <iostream>
+#include <iterator>
 #include <map>
 #include <memory>
 #include <sstream>
 #include <string>
+#include <type_traits>
 #include <utility>
 #include <vector>
 
@@ -25,7 +27,6 @@
 #include "cmConsoleBuf.h"
 #include "cmDocumentation.h"
 #include "cmDocumentationEntry.h"
-#include "cmDocumentationFormatter.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmState.h"
@@ -36,19 +37,14 @@
 #include "cmake.h"
 
 namespace {
-const char* cmDocumentationName[][2] = {
-  { nullptr, "  cpack - Packaging driver provided by CMake." },
-  { nullptr, nullptr }
+const cmDocumentationEntry cmDocumentationName = {
+  {},
+  "  cpack - Packaging driver provided by CMake."
 };
 
-const char* cmDocumentationUsage[][2] = {
-  // clang-format off
-  { nullptr, "  cpack [options]" },
-  { nullptr, nullptr }
-  // clang-format on
-};
+const cmDocumentationEntry cmDocumentationUsage = { {}, "  cpack [options]" };
 
-const char* cmDocumentationOptions[][2] = {
+const cmDocumentationEntry cmDocumentationOptions[14] = {
   { "-G <generators>", "Override/define CPACK_GENERATOR" },
   { "-C <Configuration>", "Specify the project configuration" },
   { "-D <var>=<value>", "Set a CPack variable." },
@@ -62,14 +58,30 @@
   { "-B <packageDirectory>", "Override/define CPACK_PACKAGE_DIRECTORY" },
   { "--vendor <vendorName>", "Override/define CPACK_PACKAGE_VENDOR" },
   { "--preset", "Read arguments from a package preset" },
-  { "--list-presets", "List available package presets" },
-  { nullptr, nullptr }
+  { "--list-presets", "List available package presets" }
 };
 
 void cpackProgressCallback(const std::string& message, float /*unused*/)
 {
-  std::cout << "-- " << message << std::endl;
+  std::cout << "-- " << message << '\n';
 }
+
+std::vector<cmDocumentationEntry> makeGeneratorDocs(
+  const cmCPackGeneratorFactory& gf)
+{
+  const auto& generators = gf.GetGeneratorsList();
+
+  std::vector<cmDocumentationEntry> docs;
+  docs.reserve(generators.size());
+
+  std::transform(
+    generators.cbegin(), generators.cend(), std::back_inserter(docs),
+    [](const std::decay<decltype(generators)>::type::value_type& gen) {
+      return cmDocumentationEntry{ gen.first, gen.second };
+    });
+  return docs;
+}
+
 } // namespace
 
 // this is CPack.
@@ -101,8 +113,7 @@
 
   if (cmSystemTools::GetCurrentWorkingDirectory().empty()) {
     cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
-                "Current working directory cannot be established."
-                  << std::endl);
+                "Current working directory cannot be established.\n");
     return 1;
   }
 
@@ -129,14 +140,14 @@
   auto const verboseLambda = [&log](const std::string&, cmake*,
                                     cmMakefile*) -> bool {
     log.SetVerbose(true);
-    cmCPack_Log(&log, cmCPackLog::LOG_OUTPUT, "Enable Verbose" << std::endl);
+    cmCPack_Log(&log, cmCPackLog::LOG_OUTPUT, "Enable Verbose\n");
     return true;
   };
 
   auto const debugLambda = [&log](const std::string&, cmake*,
                                   cmMakefile*) -> bool {
     log.SetDebug(true);
-    cmCPack_Log(&log, cmCPackLog::LOG_OUTPUT, "Enable Debug" << std::endl);
+    cmCPack_Log(&log, cmCPackLog::LOG_OUTPUT, "Enable Debug\n");
     return true;
   };
 
@@ -194,26 +205,25 @@
                      CommandArgument::setToValue(preset) },
     CommandArgument{ "--list-presets", CommandArgument::Values::Zero,
                      CommandArgument::setToTrue(listPresets) },
-    CommandArgument{
-      "-D", CommandArgument::Values::One,
-      [&log, &definitions](const std::string& arg, cmake*,
-                           cmMakefile*) -> bool {
-        std::string value = arg;
-        size_t pos = value.find_first_of('=');
-        if (pos == std::string::npos) {
-          cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
-                      "Please specify CPack definitions as: KEY=VALUE"
-                        << std::endl);
-          return false;
-        }
-        std::string key = value.substr(0, pos);
-        value.erase(0, pos + 1);
-        definitions[key] = value;
-        cmCPack_Log(&log, cmCPackLog::LOG_DEBUG,
-                    "Set CPack variable: " << key << " to \"" << value << "\""
-                                           << std::endl);
-        return true;
-      } },
+    CommandArgument{ "-D", CommandArgument::Values::One,
+                     [&log, &definitions](const std::string& arg, cmake*,
+                                          cmMakefile*) -> bool {
+                       std::string value = arg;
+                       size_t pos = value.find_first_of('=');
+                       if (pos == std::string::npos) {
+                         cmCPack_Log(
+                           &log, cmCPackLog::LOG_ERROR,
+                           "Please specify CPack definitions as: KEY=VALUE\n");
+                         return false;
+                       }
+                       std::string key = value.substr(0, pos);
+                       value.erase(0, pos + 1);
+                       definitions[key] = value;
+                       cmCPack_Log(&log, cmCPackLog::LOG_DEBUG,
+                                   "Set CPack variable: " << key << " to \""
+                                                          << value << "\"\n");
+                       return true;
+                     } },
   };
 
   cmake cminst(cmake::RoleScript, cmState::CPack);
@@ -262,8 +272,7 @@
       cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
                   "Could not read presets from "
                     << workingDirectory << ": "
-                    << cmCMakePresetsGraph::ResultToString(result)
-                    << std::endl);
+                    << cmCMakePresetsGraph::ResultToString(result) << '\n');
       return 1;
     }
 
@@ -276,7 +285,7 @@
     if (presetPair == presetsGraph.PackagePresets.end()) {
       cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
                   "No such package preset in " << workingDirectory << ": \""
-                                               << preset << '"' << std::endl);
+                                               << preset << "\"\n");
       presetsGraph.PrintPackagePresetList(presetGeneratorsPresent);
       return 1;
     }
@@ -284,8 +293,7 @@
     if (presetPair->second.Unexpanded.Hidden) {
       cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
                   "Cannot use hidden package preset in "
-                    << workingDirectory << ": \"" << preset << '"'
-                    << std::endl);
+                    << workingDirectory << ": \"" << preset << "\"\n");
       presetsGraph.PrintPackagePresetList(presetGeneratorsPresent);
       return 1;
     }
@@ -294,7 +302,7 @@
     if (!expandedPreset) {
       cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
                   "Could not evaluate package preset \""
-                    << preset << "\": Invalid macro expansion" << std::endl);
+                    << preset << "\": Invalid macro expansion\n");
       presetsGraph.PrintPackagePresetList(presetGeneratorsPresent);
       return 1;
     }
@@ -302,8 +310,7 @@
     if (!expandedPreset->ConditionResult) {
       cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
                   "Cannot use disabled package preset in "
-                    << workingDirectory << ": \"" << preset << '"'
-                    << std::endl);
+                    << workingDirectory << ": \"" << preset << "\"\n");
       presetsGraph.PrintPackagePresetList(presetGeneratorsPresent);
       return 1;
     }
@@ -320,7 +327,7 @@
       cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
                   "No such configure preset in "
                     << workingDirectory << ": \""
-                    << expandedPreset->ConfigurePreset << '"' << std::endl);
+                    << expandedPreset->ConfigurePreset << "\"\n");
       presetsGraph.PrintConfigurePresetList();
       return 1;
     }
@@ -329,7 +336,7 @@
       cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
                   "Cannot use hidden configure preset in "
                     << workingDirectory << ": \""
-                    << expandedPreset->ConfigurePreset << '"' << std::endl);
+                    << expandedPreset->ConfigurePreset << "\"\n");
       presetsGraph.PrintConfigurePresetList();
       return 1;
     }
@@ -339,7 +346,7 @@
       cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
                   "Could not evaluate configure preset \""
                     << expandedPreset->ConfigurePreset
-                    << "\": Invalid macro expansion" << std::endl);
+                    << "\": Invalid macro expansion\n");
       return 1;
     }
 
@@ -395,7 +402,7 @@
   }
 
   cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE,
-              "Read CPack config file: " << cpackConfigFile << std::endl);
+              "Read CPack config file: " << cpackConfigFile << '\n');
 
   bool cpackConfigFileSpecified = true;
   if (cpackConfigFile.empty()) {
@@ -423,7 +430,7 @@
       globalMF.GetModulesFile("CMakeDetermineSystem.cmake");
     if (!globalMF.ReadListFile(systemFile)) {
       cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
-                  "Error reading CMakeDetermineSystem.cmake" << std::endl);
+                  "Error reading CMakeDetermineSystem.cmake\n");
       return 1;
     }
 
@@ -431,8 +438,7 @@
       globalMF.GetModulesFile("CMakeSystemSpecificInformation.cmake");
     if (!globalMF.ReadListFile(systemFile)) {
       cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
-                  "Error reading CMakeSystemSpecificInformation.cmake"
-                    << std::endl);
+                  "Error reading CMakeSystemSpecificInformation.cmake\n");
       return 1;
     }
 
@@ -444,17 +450,17 @@
       cpackConfigFile = cmSystemTools::CollapseFullPath(cpackConfigFile);
       cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE,
                   "Read CPack configuration file: " << cpackConfigFile
-                                                    << std::endl);
+                                                    << '\n');
       if (!globalMF.ReadListFile(cpackConfigFile)) {
         cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
-                    "Problem reading CPack config file: \""
-                      << cpackConfigFile << "\"" << std::endl);
+                    "Problem reading CPack config file: \"" << cpackConfigFile
+                                                            << "\"\n");
         return 1;
       }
     } else if (cpackConfigFileSpecified) {
       cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
                   "Cannot find CPack config file: \"" << cpackConfigFile
-                                                      << "\"" << std::endl);
+                                                      << "\"\n");
       return 1;
     }
 
@@ -503,17 +509,17 @@
     cmValue genList = globalMF.GetDefinition("CPACK_GENERATOR");
     if (!genList) {
       cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
-                  "CPack generator not specified" << std::endl);
+                  "CPack generator not specified\n");
     } else {
       std::vector<std::string> generatorsVector = cmExpandedList(*genList);
       for (std::string const& gen : generatorsVector) {
         cmMakefile::ScopePushPop raii(&globalMF);
         cmMakefile* mf = &globalMF;
         cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE,
-                    "Specified generator: " << gen << std::endl);
+                    "Specified generator: " << gen << '\n');
         if (!mf->GetDefinition("CPACK_PACKAGE_NAME")) {
           cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
-                      "CPack project name not specified" << std::endl);
+                      "CPack project name not specified" << '\n');
           parsed = false;
         }
         if (parsed &&
@@ -522,13 +528,11 @@
                mf->GetDefinition("CPACK_PACKAGE_VERSION_MINOR") &&
                mf->GetDefinition("CPACK_PACKAGE_VERSION_PATCH")))) {
           cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
-                      "CPack project version not specified"
-                        << std::endl
-                        << "Specify CPACK_PACKAGE_VERSION, or "
-                           "CPACK_PACKAGE_VERSION_MAJOR, "
-                           "CPACK_PACKAGE_VERSION_MINOR, and "
-                           "CPACK_PACKAGE_VERSION_PATCH."
-                        << std::endl);
+                      "CPack project version not specified\n"
+                      "Specify CPACK_PACKAGE_VERSION, or "
+                      "CPACK_PACKAGE_VERSION_MAJOR, "
+                      "CPACK_PACKAGE_VERSION_MINOR, and "
+                      "CPACK_PACKAGE_VERSION_PATCH.\n");
           parsed = false;
         }
         if (parsed) {
@@ -539,19 +543,12 @@
             cpackGenerator->SetTraceExpand(cminst.GetTraceExpand());
           } else {
             cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
-                        "Could not create CPack generator: " << gen
-                                                             << std::endl);
+                        "Could not create CPack generator: " << gen << '\n');
             // Print out all the valid generators
             cmDocumentation generatorDocs;
-            std::vector<cmDocumentationEntry> v;
-            for (auto const& g : generators.GetGeneratorsList()) {
-              cmDocumentationEntry e;
-              e.Name = g.first;
-              e.Brief = g.second;
-              v.push_back(std::move(e));
-            }
-            generatorDocs.SetSection("Generators", v);
-            std::cerr << "\n";
+            generatorDocs.SetSection("Generators",
+                                     makeGeneratorDocs(generators));
+            std::cerr << '\n';
             generatorDocs.PrintDocumentation(cmDocumentation::ListGenerators,
                                              std::cerr);
             parsed = false;
@@ -559,8 +556,7 @@
 
           if (parsed && !cpackGenerator->Initialize(gen, mf)) {
             cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
-                        "Cannot initialize the generator " << gen
-                                                           << std::endl);
+                        "Cannot initialize the generator " << gen << '\n');
             parsed = false;
           }
 
@@ -573,17 +569,16 @@
               "Please specify build tree of the project that uses CMake "
               "using CPACK_INSTALL_CMAKE_PROJECTS, specify "
               "CPACK_INSTALL_COMMANDS, CPACK_INSTALL_SCRIPT, or "
-              "CPACK_INSTALLED_DIRECTORIES."
-                << std::endl);
+              "CPACK_INSTALLED_DIRECTORIES.\n");
             parsed = false;
           }
           if (parsed) {
             cmValue projName = mf->GetDefinition("CPACK_PACKAGE_NAME");
             cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE,
                         "Use generator: " << cpackGenerator->GetNameOfClass()
-                                          << std::endl);
+                                          << '\n');
             cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE,
-                        "For project: " << *projName << std::endl);
+                        "For project: " << *projName << '\n');
 
             cmValue projVersion = mf->GetDefinition("CPACK_PACKAGE_VERSION");
             if (!projVersion) {
@@ -594,7 +589,7 @@
               cmValue projVersionPatch =
                 mf->GetDefinition("CPACK_PACKAGE_VERSION_PATCH");
               std::ostringstream ostr;
-              ostr << *projVersionMajor << "." << *projVersionMinor << "."
+              ostr << *projVersionMajor << "." << *projVersionMinor << '.'
                    << *projVersionPatch;
               mf->AddDefinition("CPACK_PACKAGE_VERSION", ostr.str());
             }
@@ -603,7 +598,7 @@
             if (!res) {
               cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
                           "Error when generating package: " << *projName
-                                                            << std::endl);
+                                                            << '\n');
               return 1;
             }
           }
@@ -618,27 +613,13 @@
    */
   if (help) {
     // Construct and print requested documentation.
-
     doc.SetName("cpack");
     doc.SetSection("Name", cmDocumentationName);
     doc.SetSection("Usage", cmDocumentationUsage);
     doc.PrependSection("Options", cmDocumentationOptions);
-
-    std::vector<cmDocumentationEntry> v;
-    for (auto const& g : generators.GetGeneratorsList()) {
-      cmDocumentationEntry e;
-      e.Name = g.first;
-      e.Brief = g.second;
-      v.push_back(std::move(e));
-    }
-    doc.SetSection("Generators", v);
-
-    return doc.PrintRequestedDocumentation(std::cout) ? 0 : 1;
+    doc.SetSection("Generators", makeGeneratorDocs(generators));
+    return !doc.PrintRequestedDocumentation(std::cout);
   }
 
-  if (cmSystemTools::GetErrorOccurredFlag()) {
-    return 1;
-  }
-
-  return 0;
+  return int(cmSystemTools::GetErrorOccurredFlag());
 }
diff --git a/Source/CursesDialog/ccmake.cxx b/Source/CursesDialog/ccmake.cxx
index 70ed648..18c1a80 100644
--- a/Source/CursesDialog/ccmake.cxx
+++ b/Source/CursesDialog/ccmake.cxx
@@ -16,45 +16,38 @@
 #include "cmCursesMainForm.h"
 #include "cmCursesStandardIncludes.h"
 #include "cmDocumentation.h"
-#include "cmDocumentationEntry.h" // IWYU pragma: keep
+#include "cmDocumentationEntry.h"
 #include "cmMessageMetadata.h"
 #include "cmState.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-static const char* cmDocumentationName[][2] = {
-  { nullptr, "  ccmake - Curses Interface for CMake." },
-  { nullptr, nullptr }
+namespace {
+const cmDocumentationEntry cmDocumentationName = {
+  {},
+  "  ccmake - Curses Interface for CMake."
 };
 
-static const char* cmDocumentationUsage[][2] = {
-  { nullptr,
+const cmDocumentationEntry cmDocumentationUsage[2] = {
+  { {},
     "  ccmake <path-to-source>\n"
     "  ccmake <path-to-existing-build>" },
-  { nullptr,
+  { {},
     "Specify a source directory to (re-)generate a build system for "
     "it in the current working directory.  Specify an existing build "
     "directory to re-generate its build system." },
-  { nullptr, nullptr }
 };
 
-static const char* cmDocumentationUsageNote[][2] = {
-  { nullptr, "Run 'ccmake --help' for more information." },
-  { nullptr, nullptr }
+const cmDocumentationEntry cmDocumentationUsageNote = {
+  {},
+  "Run 'ccmake --help' for more information."
 };
 
-static const char* cmDocumentationOptions[][2] = {
-  CMAKE_STANDARD_OPTIONS_TABLE,
-  { nullptr, nullptr }
-};
-
-cmCursesForm* cmCursesForm::CurrentForm = nullptr;
-
 #ifndef _WIN32
 extern "C" {
 
-static void onsig(int /*unused*/)
+void onsig(int /*unused*/)
 {
   if (cmCursesForm::CurrentForm) {
     cmCursesForm::CurrentForm->HandleResize();
@@ -63,6 +56,9 @@
 }
 }
 #endif // _WIN32
+} // anonymous namespace
+
+cmCursesForm* cmCursesForm::CurrentForm = nullptr;
 
 int main(int argc, char const* const* argv)
 {
@@ -89,8 +85,8 @@
       doc.AppendSection("Usage", cmDocumentationUsageNote);
     }
     doc.AppendSection("Generators", generators);
-    doc.PrependSection("Options", cmDocumentationOptions);
-    return doc.PrintRequestedDocumentation(std::cout) ? 0 : 1;
+    doc.PrependSection("Options", cmake::CMAKE_STANDARD_OPTIONS_TABLE);
+    return !doc.PrintRequestedDocumentation(std::cout);
   }
 
   bool debug = false;
diff --git a/Source/QtDialog/CMakeSetup.cxx b/Source/QtDialog/CMakeSetup.cxx
index 591b793..50e8e3a 100644
--- a/Source/QtDialog/CMakeSetup.cxx
+++ b/Source/QtDialog/CMakeSetup.cxx
@@ -22,26 +22,27 @@
 #include "cmSystemTools.h" // IWYU pragma: keep
 #include "cmake.h"
 
-static const char* cmDocumentationName[][2] = { { nullptr,
-                                                  "  cmake-gui - CMake GUI." },
-                                                { nullptr, nullptr } };
-
-static const char* cmDocumentationUsage[][2] = {
-  { nullptr,
-    "  cmake-gui [options]\n"
-    "  cmake-gui [options] <path-to-source>\n"
-    "  cmake-gui [options] <path-to-existing-build>\n"
-    "  cmake-gui [options] -S <path-to-source> -B <path-to-build>\n"
-    "  cmake-gui [options] --browse-manual\n" },
-  { nullptr, nullptr }
+namespace {
+const cmDocumentationEntry cmDocumentationName = {
+  {},
+  "  cmake-gui - CMake GUI."
 };
 
-static const char* cmDocumentationOptions[][2] = {
+const cmDocumentationEntry cmDocumentationUsage = {
+  {},
+  "  cmake-gui [options]\n"
+  "  cmake-gui [options] <path-to-source>\n"
+  "  cmake-gui [options] <path-to-existing-build>\n"
+  "  cmake-gui [options] -S <path-to-source> -B <path-to-build>\n"
+  "  cmake-gui [options] --browse-manual"
+};
+
+const cmDocumentationEntry cmDocumentationOptions[3] = {
   { "-S <path-to-source>", "Explicitly specify a source directory." },
   { "-B <path-to-build>", "Explicitly specify a build directory." },
-  { "--preset=<preset>", "Specify a configure preset." },
-  { nullptr, nullptr }
+  { "--preset=<preset>", "Specify a configure preset." }
 };
+} // anonymous namespace
 
 #if defined(Q_OS_MAC)
 static int cmOSXInstall(std::string dir);
@@ -91,7 +92,7 @@
     doc.AppendSection("Generators", generators);
     doc.PrependSection("Options", cmDocumentationOptions);
 
-    return (doc.PrintRequestedDocumentation(std::cout) ? 0 : 1);
+    return !doc.PrintRequestedDocumentation(std::cout);
   }
 
 #if defined(Q_OS_MAC)
@@ -252,6 +253,7 @@
 #  include <unistd.h>
 
 #  include "cm_sys_stat.h"
+
 static bool cmOSXInstall(std::string const& dir, std::string const& tool)
 {
   if (tool.empty()) {
@@ -277,6 +279,7 @@
             << "': " << strerror(err) << "\n";
   return false;
 }
+
 static int cmOSXInstall(std::string dir)
 {
   if (!cmHasLiteralSuffix(dir, "/")) {
diff --git a/Source/cmDocumentation.cxx b/Source/cmDocumentation.cxx
index d466a12..77c5295 100644
--- a/Source/cmDocumentation.cxx
+++ b/Source/cmDocumentation.cxx
@@ -16,7 +16,8 @@
 #include "cmSystemTools.h"
 #include "cmVersion.h"
 
-static const char* cmDocumentationStandardOptions[][2] = {
+namespace {
+const cmDocumentationEntry cmDocumentationStandardOptions[20] = {
   { "-h,-H,--help,-help,-usage,/?", "Print usage information and exit." },
   { "--version,-version,/V [<file>]", "Print version number and exit." },
   { "--help-full [<file>]", "Print all help manuals and exit." },
@@ -42,22 +43,27 @@
   { "--help-variable var [<file>]", "Print help for one variable and exit." },
   { "--help-variable-list [<file>]",
     "List variables with help available and exit." },
-  { "--help-variables [<file>]", "Print cmake-variables manual and exit." },
-  { nullptr, nullptr }
+  { "--help-variables [<file>]", "Print cmake-variables manual and exit." }
 };
 
-static const char* cmDocumentationCPackGeneratorsHeader[][2] = {
-  { nullptr, "The following generators are available on this platform:" },
-  { nullptr, nullptr }
+const cmDocumentationEntry cmDocumentationCPackGeneratorsHeader = {
+  {},
+  "The following generators are available on this platform:"
 };
 
-static const char* cmDocumentationCMakeGeneratorsHeader[][2] = {
-  { nullptr,
-    "The following generators are available on this platform (* marks "
-    "default):" },
-  { nullptr, nullptr }
+const cmDocumentationEntry cmDocumentationCMakeGeneratorsHeader = {
+  {},
+  "The following generators are available on this platform (* marks "
+  "default):"
 };
 
+bool isOption(const char* arg)
+{
+  return ((arg[0] == '-') || (strcmp(arg, "/V") == 0) ||
+          (strcmp(arg, "/?") == 0));
+}
+} // anonymous namespace
+
 cmDocumentation::cmDocumentation()
 {
   this->addCommonStandardDocSections();
@@ -148,14 +154,6 @@
   return result;
 }
 
-#define GET_OPT_ARGUMENT(target)                                              \
-  do {                                                                        \
-    if ((i + 1 < argc) && !this->IsOption(argv[i + 1])) {                     \
-      (target) = argv[i + 1];                                                 \
-      i = i + 1;                                                              \
-    };                                                                        \
-  } while (false)
-
 void cmDocumentation::WarnFormFromFilename(
   cmDocumentation::RequestedHelpItem& request, bool& result)
 {
@@ -217,6 +215,14 @@
     return true;
   }
 
+  auto get_opt_argument = [=](const int nextIdx, std::string& target) -> bool {
+    if ((nextIdx < argc) && !isOption(argv[nextIdx])) {
+      target = argv[nextIdx];
+      return true;
+    }
+    return false;
+  };
+
   // Search for supported help options.
 
   bool result = false;
@@ -230,7 +236,7 @@
         (strcmp(argv[i], "/?") == 0) || (strcmp(argv[i], "-usage") == 0) ||
         (strcmp(argv[i], "-h") == 0) || (strcmp(argv[i], "-H") == 0)) {
       help.HelpType = cmDocumentation::Help;
-      GET_OPT_ARGUMENT(help.Argument);
+      i += int(get_opt_argument(i + 1, help.Argument));
       help.Argument = cmSystemTools::LowerCase(help.Argument);
       // special case for single command
       if (!help.Argument.empty()) {
@@ -239,25 +245,25 @@
     } else if (strcmp(argv[i], "--help-properties") == 0) {
       help.HelpType = cmDocumentation::OneManual;
       help.Argument = "cmake-properties.7";
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Filename));
       this->WarnFormFromFilename(help, result);
     } else if (strcmp(argv[i], "--help-policies") == 0) {
       help.HelpType = cmDocumentation::OneManual;
       help.Argument = "cmake-policies.7";
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Filename));
       this->WarnFormFromFilename(help, result);
     } else if (strcmp(argv[i], "--help-variables") == 0) {
       help.HelpType = cmDocumentation::OneManual;
       help.Argument = "cmake-variables.7";
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Filename));
       this->WarnFormFromFilename(help, result);
     } else if (strcmp(argv[i], "--help-modules") == 0) {
       help.HelpType = cmDocumentation::OneManual;
       help.Argument = "cmake-modules.7";
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Filename));
       this->WarnFormFromFilename(help, result);
     } else if (strcmp(argv[i], "--help-custom-modules") == 0) {
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Filename));
       cmSystemTools::Message(
         "Warning: --help-custom-modules no longer supported");
       if (help.Filename.empty()) {
@@ -271,83 +277,79 @@
     } else if (strcmp(argv[i], "--help-commands") == 0) {
       help.HelpType = cmDocumentation::OneManual;
       help.Argument = "cmake-commands.7";
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Filename));
       this->WarnFormFromFilename(help, result);
     } else if (strcmp(argv[i], "--help-compatcommands") == 0) {
-      GET_OPT_ARGUMENT(help.Filename);
       cmSystemTools::Message(
         "Warning: --help-compatcommands no longer supported");
       return true;
     } else if (strcmp(argv[i], "--help-full") == 0) {
       help.HelpType = cmDocumentation::Full;
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Filename));
       this->WarnFormFromFilename(help, result);
     } else if (strcmp(argv[i], "--help-html") == 0) {
-      GET_OPT_ARGUMENT(help.Filename);
       cmSystemTools::Message("Warning: --help-html no longer supported");
       return true;
     } else if (strcmp(argv[i], "--help-man") == 0) {
-      GET_OPT_ARGUMENT(help.Filename);
       cmSystemTools::Message("Warning: --help-man no longer supported");
       return true;
     } else if (strcmp(argv[i], "--help-command") == 0) {
       help.HelpType = cmDocumentation::OneCommand;
-      GET_OPT_ARGUMENT(help.Argument);
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Argument));
+      i += int(get_opt_argument(i + 1, help.Filename));
       help.Argument = cmSystemTools::LowerCase(help.Argument);
       this->WarnFormFromFilename(help, result);
     } else if (strcmp(argv[i], "--help-module") == 0) {
       help.HelpType = cmDocumentation::OneModule;
-      GET_OPT_ARGUMENT(help.Argument);
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Argument));
+      i += int(get_opt_argument(i + 1, help.Filename));
       this->WarnFormFromFilename(help, result);
     } else if (strcmp(argv[i], "--help-property") == 0) {
       help.HelpType = cmDocumentation::OneProperty;
-      GET_OPT_ARGUMENT(help.Argument);
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Argument));
+      i += int(get_opt_argument(i + 1, help.Filename));
       this->WarnFormFromFilename(help, result);
     } else if (strcmp(argv[i], "--help-policy") == 0) {
       help.HelpType = cmDocumentation::OnePolicy;
-      GET_OPT_ARGUMENT(help.Argument);
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Argument));
+      i += int(get_opt_argument(i + 1, help.Filename));
       this->WarnFormFromFilename(help, result);
     } else if (strcmp(argv[i], "--help-variable") == 0) {
       help.HelpType = cmDocumentation::OneVariable;
-      GET_OPT_ARGUMENT(help.Argument);
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Argument));
+      i += int(get_opt_argument(i + 1, help.Filename));
       this->WarnFormFromFilename(help, result);
     } else if (strcmp(argv[i], "--help-manual") == 0) {
       help.HelpType = cmDocumentation::OneManual;
-      GET_OPT_ARGUMENT(help.Argument);
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Argument));
+      i += int(get_opt_argument(i + 1, help.Filename));
       this->WarnFormFromFilename(help, result);
     } else if (strcmp(argv[i], "--help-command-list") == 0) {
       help.HelpType = cmDocumentation::ListCommands;
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Filename));
     } else if (strcmp(argv[i], "--help-module-list") == 0) {
       help.HelpType = cmDocumentation::ListModules;
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Filename));
     } else if (strcmp(argv[i], "--help-property-list") == 0) {
       help.HelpType = cmDocumentation::ListProperties;
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Filename));
     } else if (strcmp(argv[i], "--help-variable-list") == 0) {
       help.HelpType = cmDocumentation::ListVariables;
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Filename));
     } else if (strcmp(argv[i], "--help-policy-list") == 0) {
       help.HelpType = cmDocumentation::ListPolicies;
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Filename));
     } else if (strcmp(argv[i], "--help-manual-list") == 0) {
       help.HelpType = cmDocumentation::ListManuals;
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Filename));
     } else if (strcmp(argv[i], "--copyright") == 0) {
-      GET_OPT_ARGUMENT(help.Filename);
       cmSystemTools::Message("Warning: --copyright no longer supported");
       return true;
     } else if ((strcmp(argv[i], "--version") == 0) ||
                (strcmp(argv[i], "-version") == 0) ||
                (strcmp(argv[i], "/V") == 0)) {
       help.HelpType = cmDocumentation::Version;
-      GET_OPT_ARGUMENT(help.Filename);
+      i += int(get_opt_argument(i + 1, help.Filename));
     }
     if (help.HelpType != None) {
       // This is a help option.  See if there is a file name given.
@@ -369,56 +371,12 @@
   this->SectionAtName(name) = std::move(section);
 }
 
-void cmDocumentation::SetSection(const char* name,
-                                 std::vector<cmDocumentationEntry>& docs)
-{
-  cmDocumentationSection sec{ name };
-  sec.Append(docs);
-  this->SetSection(name, std::move(sec));
-}
-
-void cmDocumentation::SetSection(const char* name, const char* docs[][2])
-{
-  cmDocumentationSection sec{ name };
-  sec.Append(docs);
-  this->SetSection(name, std::move(sec));
-}
-
-void cmDocumentation::SetSections(
-  std::map<std::string, cmDocumentationSection> sections)
-{
-  for (auto& s : sections) {
-    this->SetSection(s.first.c_str(), std::move(s.second));
-  }
-}
 cmDocumentationSection& cmDocumentation::SectionAtName(const char* name)
 {
   return this->AllSections.emplace(name, cmDocumentationSection{ name })
     .first->second;
 }
 
-void cmDocumentation::PrependSection(const char* name, const char* docs[][2])
-{
-  this->SectionAtName(name).Prepend(docs);
-}
-
-void cmDocumentation::PrependSection(const char* name,
-                                     std::vector<cmDocumentationEntry>& docs)
-{
-  this->SectionAtName(name).Prepend(docs);
-}
-
-void cmDocumentation::AppendSection(const char* name, const char* docs[][2])
-{
-  this->SectionAtName(name).Append(docs);
-}
-
-void cmDocumentation::AppendSection(const char* name,
-                                    std::vector<cmDocumentationEntry>& docs)
-{
-  this->SectionAtName(name).Append(docs);
-}
-
 void cmDocumentation::AppendSection(const char* name,
                                     cmDocumentationEntry& docs)
 {
@@ -465,7 +423,7 @@
   }
   std::sort(names.begin(), names.end());
   for (std::string const& n : names) {
-    os << n << "\n";
+    os << n << '\n';
   }
 }
 
@@ -501,7 +459,7 @@
   // Argument was not a manual.  Complain.
   os << "Argument \"" << this->CurrentArgument
      << "\" to --help-manual is not an available manual.  "
-     << "Use --help-manual-list to see all available manuals.\n";
+        "Use --help-manual-list to see all available manuals.\n";
   return false;
 }
 
@@ -520,7 +478,7 @@
   // Argument was not a command.  Complain.
   os << "Argument \"" << this->CurrentArgument
      << "\" to --help-command is not a CMake command.  "
-     << "Use --help-command-list to see all commands.\n";
+        "Use --help-command-list to see all commands.\n";
   return false;
 }
 
@@ -553,7 +511,7 @@
   }
   std::sort(modules.begin(), modules.end());
   for (std::string const& m : modules) {
-    os << m << "\n";
+    os << m << '\n';
   }
   return true;
 }
@@ -567,7 +525,7 @@
   // Argument was not a property.  Complain.
   os << "Argument \"" << this->CurrentArgument
      << "\" to --help-property is not a CMake property.  "
-     << "Use --help-property-list to see all properties.\n";
+        "Use --help-property-list to see all properties.\n";
   return false;
 }
 
@@ -601,7 +559,6 @@
 {
   const auto si = this->AllSections.find("Generators");
   if (si != this->AllSections.end()) {
-    this->Formatter.SetIndent("  ");
     this->Formatter.PrintSection(os, si->second);
   }
   return true;
@@ -616,7 +573,7 @@
   // Argument was not a variable.  Complain.
   os << "Argument \"" << this->CurrentArgument
      << "\" to --help-variable is not a defined variable.  "
-     << "Use --help-variable-list to see all defined variables.\n";
+        "Use --help-variable-list to see all defined variables.\n";
   return false;
 }
 
@@ -662,12 +619,6 @@
   return "CMake";
 }
 
-bool cmDocumentation::IsOption(const char* arg) const
-{
-  return ((arg[0] == '-') || (strcmp(arg, "/V") == 0) ||
-          (strcmp(arg, "/?") == 0));
-}
-
 bool cmDocumentation::PrintOldCustomModules(std::ostream& os)
 {
   // CheckOptions abuses the Argument field to give us the file name.
@@ -691,7 +642,7 @@
   } else if ((ext.length() == 2) && (ext[1] >= '1') && (ext[1] <= '9')) {
     /* clang-format off */
     os <<
-      ".TH " << name << " " << ext[1] << " \"" <<
+      ".TH " << name << ' ' << ext[1] << " \"" <<
       cmSystemTools::GetCurrentDateTime("%B %d, %Y") <<
       "\" \"cmake " << cmVersion::GetCMakeVersion() << "\"\n"
       ".SH NAME\n"
@@ -704,7 +655,7 @@
       ;
     /* clang-format on */
   } else {
-    os << name << "\n\n" << summary << "\n" << detail;
+    os << name << "\n\n" << summary << '\n' << detail;
   }
   return true;
 }
diff --git a/Source/cmDocumentation.h b/Source/cmDocumentation.h
index 313be32..6930986 100644
--- a/Source/cmDocumentation.h
+++ b/Source/cmDocumentation.h
@@ -7,6 +7,7 @@
 #include <iosfwd>
 #include <map>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "cmDocumentationFormatter.h"
@@ -15,9 +16,33 @@
 struct cmDocumentationEntry;
 
 /** Class to generate documentation.  */
-class cmDocumentation : public cmDocumentationEnums
+class cmDocumentation
 {
 public:
+  /** Types of help provided.  */
+  enum Type
+  {
+    None,
+    Version,
+    Usage,
+    Help,
+    Full,
+    ListManuals,
+    ListCommands,
+    ListModules,
+    ListProperties,
+    ListVariables,
+    ListPolicies,
+    ListGenerators,
+    OneManual,
+    OneCommand,
+    OneModule,
+    OneProperty,
+    OneVariable,
+    OnePolicy,
+    OldCustomModules
+  };
+
   cmDocumentation();
 
   /**
@@ -50,19 +75,26 @@
   /** Set a section of the documentation. Typical sections include Name,
       Usage, Description, Options */
   void SetSection(const char* sectionName, cmDocumentationSection section);
-  void SetSection(const char* sectionName,
-                  std::vector<cmDocumentationEntry>& docs);
-  void SetSection(const char* sectionName, const char* docs[][2]);
-  void SetSections(std::map<std::string, cmDocumentationSection> sections);
+  template <typename Iterable>
+  void SetSection(const char* sectionName, const Iterable& docs)
+  {
+    cmDocumentationSection sec{ sectionName };
+    sec.Append(docs);
+    this->SetSection(sectionName, std::move(sec));
+  }
 
   /** Add the documentation to the beginning/end of the section */
-  void PrependSection(const char* sectionName, const char* docs[][2]);
-  void PrependSection(const char* sectionName,
-                      std::vector<cmDocumentationEntry>& docs);
+  template <typename Iterable>
+  void PrependSection(const char* sectionName, const Iterable& docs)
+  {
+    this->SectionAtName(sectionName).Prepend(docs);
+  }
   void PrependSection(const char* sectionName, cmDocumentationEntry& docs);
-  void AppendSection(const char* sectionName, const char* docs[][2]);
-  void AppendSection(const char* sectionName,
-                     std::vector<cmDocumentationEntry>& docs);
+  template <typename Iterable>
+  void AppendSection(const char* sectionName, const Iterable& docs)
+  {
+    this->SectionAtName(sectionName).Append(docs);
+  }
   void AppendSection(const char* sectionName, cmDocumentationEntry& docs);
 
   /** Add common (to all tools) documentation section(s) */
@@ -102,7 +134,6 @@
   bool PrintOldCustomModules(std::ostream& os);
 
   const char* GetNameString() const;
-  bool IsOption(const char* arg) const;
 
   bool ShowGenerators;
 
@@ -114,7 +145,7 @@
 
   struct RequestedHelpItem
   {
-    cmDocumentationEnums::Type HelpType = None;
+    Type HelpType = None;
     std::string Filename;
     std::string Argument;
   };
diff --git a/Source/cmDocumentationEntry.h b/Source/cmDocumentationEntry.h
index 89a2899..d971836 100644
--- a/Source/cmDocumentationEntry.h
+++ b/Source/cmDocumentationEntry.h
@@ -9,26 +9,15 @@
 /** Standard documentation entry for cmDocumentation's formatting.  */
 struct cmDocumentationEntry
 {
-  std::string Name;
-  std::string Brief;
+#if __cplusplus <= 201103L
+  cmDocumentationEntry(const std::string& name, const std::string& brief)
+    : Name{ name }
+    , Brief{ brief }
+  {
+  }
+#endif
+
+  std::string Name = {};
+  std::string Brief = {};
   char CustomNamePrefix = ' ';
-  cmDocumentationEntry() = default;
-  cmDocumentationEntry(const char* doc[2])
-  {
-    if (doc[0]) {
-      this->Name = doc[0];
-    }
-    if (doc[1]) {
-      this->Brief = doc[1];
-    }
-  }
-  cmDocumentationEntry(const char* n, const char* b)
-  {
-    if (n) {
-      this->Name = n;
-    }
-    if (b) {
-      this->Brief = b;
-    }
-  }
 };
diff --git a/Source/cmDocumentationFormatter.cxx b/Source/cmDocumentationFormatter.cxx
index 732637e..70ba1fc 100644
--- a/Source/cmDocumentationFormatter.cxx
+++ b/Source/cmDocumentationFormatter.cxx
@@ -2,7 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmDocumentationFormatter.h"
 
-#include <cstring>
+#include <algorithm>
+#include <cassert>
 #include <iomanip>
 #include <ostream>
 #include <string>
@@ -11,178 +12,207 @@
 #include "cmDocumentationEntry.h"
 #include "cmDocumentationSection.h"
 
-cmDocumentationFormatter::cmDocumentationFormatter() = default;
-
-cmDocumentationFormatter::~cmDocumentationFormatter() = default;
+namespace {
+const char* skipSpaces(const char* ptr)
+{
+  assert(ptr);
+  for (; *ptr == ' '; ++ptr) {
+    ;
+  }
+  return ptr;
+}
+const char* skipToSpace(const char* ptr)
+{
+  assert(ptr);
+  for (; *ptr && (*ptr != '\n') && (*ptr != ' '); ++ptr) {
+    ;
+  }
+  return ptr;
+}
+}
 
 void cmDocumentationFormatter::PrintFormatted(std::ostream& os,
-                                              const char* text)
+                                              std::string const& text) const
 {
-  if (!text) {
+  if (text.empty()) {
     return;
   }
-  const char* ptr = text;
-  while (*ptr) {
-    // Any ptrs starting in a space are treated as preformatted text.
-    std::string preformatted;
-    while (*ptr == ' ') {
-      for (char ch = *ptr; ch && ch != '\n'; ++ptr, ch = *ptr) {
-        preformatted.append(1, ch);
-      }
-      if (*ptr) {
-        ++ptr;
-        preformatted.append(1, '\n');
-      }
-    }
-    if (!preformatted.empty()) {
-      this->PrintPreformatted(os, preformatted.c_str());
+
+  struct Buffer
+  {
+    // clang-format off
+    using PrinterFn = void (cmDocumentationFormatter::*)(
+        std::ostream&, std::string const&
+      ) const;
+    // clang-format on
+    std::string collected;
+    const PrinterFn printer;
+  };
+  // const auto NORMAL_IDX = 0u;
+  const auto PREFORMATTED_IDX = 1u;
+  const auto HANDLERS_SIZE = 2u;
+  Buffer buffers[HANDLERS_SIZE] = {
+    { {}, &cmDocumentationFormatter::PrintParagraph },
+    { {}, &cmDocumentationFormatter::PrintPreformatted }
+  };
+
+  const auto padding = std::string(this->TextIndent, ' ');
+
+  for (std::size_t pos = 0u, eol = 0u; pos < text.size(); pos = eol) {
+    const auto current_idx = std::size_t(text[pos] == ' ');
+    // size_t(!bool(current_idx))
+    const auto other_idx = current_idx ^ 1u;
+
+    // Flush the other buffer if anything has been collected
+    if (!buffers[other_idx].collected.empty()) {
+      // NOTE Whatever the other index is, the current buffered
+      // string expected to be empty.
+      assert(buffers[current_idx].collected.empty());
+
+      (this->*buffers[other_idx].printer)(os, buffers[other_idx].collected);
+      buffers[other_idx].collected.clear();
     }
 
-    // Other ptrs are treated as paragraphs.
-    std::string paragraph;
-    for (char ch = *ptr; ch && ch != '\n'; ++ptr, ch = *ptr) {
-      paragraph.append(1, ch);
+    // ATTENTION The previous implementation had called `PrintParagraph()`
+    // **for every processed (char by char) input line**.
+    // The method unconditionally append the `\n' character after the
+    // printed text. To keep the backward-compatible behavior it's needed to
+    // add the '\n' character to the previously collected line...
+    if (!buffers[current_idx].collected.empty() &&
+        current_idx != PREFORMATTED_IDX) {
+      buffers[current_idx].collected += '\n';
     }
-    if (*ptr) {
-      ++ptr;
-      paragraph.append(1, '\n');
+
+    // Lookup EOL
+    eol = text.find('\n', pos);
+    if (current_idx == PREFORMATTED_IDX) {
+      buffers[current_idx].collected.append(padding);
     }
-    if (!paragraph.empty()) {
-      this->PrintParagraph(os, paragraph.c_str());
+    buffers[current_idx].collected.append(
+      text, pos, eol == std::string::npos ? eol : ++eol - pos);
+  }
+
+  for (auto& buf : buffers) {
+    if (!buf.collected.empty()) {
+      (this->*buf.printer)(os, buf.collected);
     }
   }
 }
 
 void cmDocumentationFormatter::PrintPreformatted(std::ostream& os,
-                                                 const char* text)
+                                                 std::string const& text) const
 {
-  bool newline = true;
-  for (const char* ptr = text; *ptr; ++ptr) {
-    if (newline && *ptr != '\n') {
-      os << this->TextIndent;
-      newline = false;
-    }
-    os << *ptr;
-    if (*ptr == '\n') {
-      newline = true;
-    }
-  }
-  os << "\n";
+  os << text << '\n';
 }
 
 void cmDocumentationFormatter::PrintParagraph(std::ostream& os,
-                                              const char* text)
+                                              std::string const& text) const
 {
-  os << this->TextIndent;
+  if (this->TextIndent) {
+    os << std::string(this->TextIndent, ' ');
+  }
   this->PrintColumn(os, text);
-  os << "\n";
+  os << '\n';
 }
 
-void cmDocumentationFormatter::SetIndent(const char* indent)
-{
-  this->TextIndent = indent;
-}
-
-void cmDocumentationFormatter::PrintColumn(std::ostream& os, const char* text)
+void cmDocumentationFormatter::PrintColumn(std::ostream& os,
+                                           std::string const& text) const
 {
   // Print text arranged in an indented column of fixed width.
-  const char* l = text;
-  long column = 0;
   bool newSentence = false;
   bool firstLine = true;
-  int width = this->TextWidth - static_cast<int>(strlen(this->TextIndent));
+
+  assert(this->TextIndent < this->TextWidth);
+  const std::ptrdiff_t width = this->TextWidth - this->TextIndent;
+  std::ptrdiff_t column = 0;
 
   // Loop until the end of the text.
-  while (*l) {
-    // Parse the next word.
-    const char* r = l;
-    while (*r && (*r != '\n') && (*r != ' ')) {
-      ++r;
-    }
-
+  for (const char *l = text.c_str(), *r = skipToSpace(text.c_str()); *l;
+       l = skipSpaces(r), r = skipToSpace(l)) {
     // Does it fit on this line?
-    if (r - l < (width - column - (newSentence ? 1 : 0))) {
+    if (r - l < width - column - std::ptrdiff_t(newSentence)) {
       // Word fits on this line.
       if (r > l) {
         if (column) {
           // Not first word on line.  Separate from the previous word
           // by a space, or two if this is a new sentence.
-          if (newSentence) {
-            os << "  ";
-            column += 2;
-          } else {
-            os << " ";
-            column += 1;
-          }
-        } else {
+          os << &("  "[std::size_t(!newSentence)]);
+          column += 1u + std::ptrdiff_t(newSentence);
+        } else if (!firstLine && this->TextIndent) {
           // First word on line.  Print indentation unless this is the
           // first line.
-          os << (firstLine ? "" : this->TextIndent);
+          os << std::string(this->TextIndent, ' ');
         }
 
         // Print the word.
-        os.write(l, static_cast<long>(r - l));
+        os.write(l, r - l);
         newSentence = (*(r - 1) == '.');
       }
 
       if (*r == '\n') {
         // Text provided a newline.  Start a new line.
-        os << "\n";
+        os << '\n';
         ++r;
         column = 0;
         firstLine = false;
       } else {
         // No provided newline.  Continue this line.
-        column += static_cast<long>(r - l);
+        column += r - l;
       }
     } else {
       // Word does not fit on this line.  Start a new line.
-      os << "\n";
+      os << '\n';
       firstLine = false;
       if (r > l) {
-        os << this->TextIndent;
-        os.write(l, static_cast<long>(r - l));
-        column = static_cast<long>(r - l);
+        os << std::string(this->TextIndent, ' ');
+        os.write(l, r - l);
+        column = r - l;
         newSentence = (*(r - 1) == '.');
       } else {
         column = 0;
       }
     }
-
     // Move to beginning of next word.  Skip over whitespace.
-    l = r;
-    while (*l == ' ') {
-      ++l;
-    }
   }
 }
 
 void cmDocumentationFormatter::PrintSection(
   std::ostream& os, cmDocumentationSection const& section)
 {
-  os << section.GetName() << "\n";
+  const std::size_t PREFIX_SIZE =
+    sizeof(cmDocumentationEntry::CustomNamePrefix) + 1u;
+  // length of the "= " literal (see below)
+  const std::size_t SUFFIX_SIZE = 2u;
+  // legacy magic number ;-)
+  const std::size_t NAME_SIZE = 29u;
 
-  const std::vector<cmDocumentationEntry>& entries = section.GetEntries();
-  for (cmDocumentationEntry const& entry : entries) {
+  const std::size_t PADDING_SIZE = PREFIX_SIZE + SUFFIX_SIZE;
+  const std::size_t TITLE_SIZE = NAME_SIZE + PADDING_SIZE;
+
+  const auto savedIndent = this->TextIndent;
+
+  os << section.GetName() << '\n';
+
+  for (cmDocumentationEntry const& entry : section.GetEntries()) {
     if (!entry.Name.empty()) {
-      os << std::setw(2) << std::left << entry.CustomNamePrefix << entry.Name;
-      this->TextIndent = "                                 ";
-      int align = static_cast<int>(strlen(this->TextIndent)) - 4;
-      for (int i = static_cast<int>(entry.Name.size()); i < align; ++i) {
-        os << " ";
-      }
-      if (entry.Name.size() > strlen(this->TextIndent) - 4) {
-        os << "\n";
-        os.write(this->TextIndent, strlen(this->TextIndent) - 2);
+      this->TextIndent = TITLE_SIZE;
+      os << std::setw(PREFIX_SIZE) << std::left << entry.CustomNamePrefix
+         << std::setw(int(std::max(NAME_SIZE, entry.Name.size())))
+         << entry.Name;
+      if (entry.Name.size() > NAME_SIZE) {
+        os << '\n' << std::setw(int(this->TextIndent - PREFIX_SIZE)) << ' ';
       }
       os << "= ";
-      this->PrintColumn(os, entry.Brief.c_str());
-      os << "\n";
+      this->PrintColumn(os, entry.Brief);
+      os << '\n';
     } else {
-      os << "\n";
-      this->TextIndent = "";
-      this->PrintFormatted(os, entry.Brief.c_str());
+      os << '\n';
+      this->TextIndent = 0u;
+      this->PrintFormatted(os, entry.Brief);
     }
   }
-  os << "\n";
+
+  os << '\n';
+
+  this->TextIndent = savedIndent;
 }
diff --git a/Source/cmDocumentationFormatter.h b/Source/cmDocumentationFormatter.h
index cb3038a..e269f6a 100644
--- a/Source/cmDocumentationFormatter.h
+++ b/Source/cmDocumentationFormatter.h
@@ -5,40 +5,7 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <iosfwd>
-
-/** This is just a helper class to make it build with MSVC 6.0.
-Actually the enums and internal classes could directly go into
-cmDocumentation, but then MSVC6 complains in RequestedHelpItem that
-cmDocumentation is an undefined type and so it doesn't know the enums.
-Moving the enums to a class which is then already completely parsed helps
-against this. */
-class cmDocumentationEnums
-{
-public:
-  /** Types of help provided.  */
-  enum Type
-  {
-    None,
-    Version,
-    Usage,
-    Help,
-    Full,
-    ListManuals,
-    ListCommands,
-    ListModules,
-    ListProperties,
-    ListVariables,
-    ListPolicies,
-    ListGenerators,
-    OneManual,
-    OneCommand,
-    OneModule,
-    OneProperty,
-    OneVariable,
-    OnePolicy,
-    OldCustomModules
-  };
-};
+#include <string>
 
 class cmDocumentationSection;
 
@@ -46,18 +13,15 @@
 class cmDocumentationFormatter
 {
 public:
-  cmDocumentationFormatter();
-  virtual ~cmDocumentationFormatter();
-  void PrintFormatted(std::ostream& os, const char* text);
-
-  virtual void PrintSection(std::ostream& os,
-                            cmDocumentationSection const& section);
-  virtual void PrintPreformatted(std::ostream& os, const char* text);
-  virtual void PrintParagraph(std::ostream& os, const char* text);
-  void PrintColumn(std::ostream& os, const char* text);
-  void SetIndent(const char* indent);
+  void SetIndent(std::size_t indent) { this->TextIndent = indent; }
+  void PrintFormatted(std::ostream& os, std::string const& text) const;
+  void PrintSection(std::ostream& os, cmDocumentationSection const& section);
 
 private:
-  int TextWidth = 77;
-  const char* TextIndent = "";
+  void PrintPreformatted(std::ostream& os, std::string const&) const;
+  void PrintParagraph(std::ostream& os, std::string const&) const;
+  void PrintColumn(std::ostream& os, std::string const&) const;
+
+  std::size_t TextWidth = 77u;
+  std::size_t TextIndent = 0u;
 };
diff --git a/Source/cmDocumentationSection.cxx b/Source/cmDocumentationSection.cxx
deleted file mode 100644
index 439da1b..0000000
--- a/Source/cmDocumentationSection.cxx
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmDocumentationSection.h"
-
-void cmDocumentationSection::Append(const char* data[][2])
-{
-  int i = 0;
-  while (data[i][1]) {
-    this->Entries.emplace_back(data[i][0], data[i][1]);
-    data += 1;
-  }
-}
-
-void cmDocumentationSection::Prepend(const char* data[][2])
-{
-  std::vector<cmDocumentationEntry> tmp;
-  int i = 0;
-  while (data[i][1]) {
-    tmp.emplace_back(data[i][0], data[i][1]);
-    data += 1;
-  }
-  this->Entries.insert(this->Entries.begin(), tmp.begin(), tmp.end());
-}
-
-void cmDocumentationSection::Append(const char* n, const char* b)
-{
-  this->Entries.emplace_back(n, b);
-}
diff --git a/Source/cmDocumentationSection.h b/Source/cmDocumentationSection.h
index 276e520..b5e24fe 100644
--- a/Source/cmDocumentationSection.h
+++ b/Source/cmDocumentationSection.h
@@ -4,11 +4,10 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <iterator>
 #include <string>
 #include <vector>
 
-#include <cmext/algorithm>
-
 #include "cmDocumentationEntry.h"
 
 // Low-level interface for custom documents:
@@ -45,21 +44,20 @@
   {
     this->Entries.push_back(entry);
   }
-  void Append(const std::vector<cmDocumentationEntry>& entries)
+
+  template <typename Iterable>
+  void Append(const Iterable& entries)
   {
-    cm::append(this->Entries, entries);
+    this->Entries.insert(std::end(this->Entries), std::begin(entries),
+                         std::end(entries));
   }
 
-  /** Append an entry to this section using NULL terminated chars */
-  void Append(const char* [][2]);
-  void Append(const char* n, const char* b);
-
   /** prepend some documentation to this section */
-  void Prepend(const char* [][2]);
-  void Prepend(const std::vector<cmDocumentationEntry>& entries)
+  template <typename Iterable>
+  void Prepend(const Iterable& entries)
   {
-    this->Entries.insert(this->Entries.begin(), entries.begin(),
-                         entries.end());
+    this->Entries.insert(std::begin(this->Entries), std::begin(entries),
+                         std::end(entries));
   }
 
 private:
diff --git a/Source/cmGeneratorExpression.cxx b/Source/cmGeneratorExpression.cxx
index 21ace89..b605350 100644
--- a/Source/cmGeneratorExpression.cxx
+++ b/Source/cmGeneratorExpression.cxx
@@ -2,6 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGeneratorExpression.h"
 
+#include <algorithm>
 #include <cassert>
 #include <memory>
 #include <utility>
@@ -226,23 +227,33 @@
   while (true) {
     std::string::size_type bPos = input.find("$<BUILD_INTERFACE:", lastPos);
     std::string::size_type iPos = input.find("$<INSTALL_INTERFACE:", lastPos);
+    std::string::size_type lPos =
+      input.find("$<BUILD_LOCAL_INTERFACE:", lastPos);
 
-    if (bPos == std::string::npos && iPos == std::string::npos) {
+    pos = std::min({ bPos, iPos, lPos });
+    if (pos == std::string::npos) {
       break;
     }
 
-    if (bPos == std::string::npos) {
-      pos = iPos;
-    } else if (iPos == std::string::npos) {
-      pos = bPos;
-    } else {
-      pos = (bPos < iPos) ? bPos : iPos;
-    }
-
     result += input.substr(lastPos, pos - lastPos);
-    const bool gotInstallInterface = input[pos + 2] == 'I';
-    pos += gotInstallInterface ? sizeof("$<INSTALL_INTERFACE:") - 1
-                               : sizeof("$<BUILD_INTERFACE:") - 1;
+    enum class FoundGenex
+    {
+      BuildInterface,
+      InstallInterface,
+      BuildLocalInterface,
+    } foundGenex = FoundGenex::BuildInterface;
+    if (pos == bPos) {
+      foundGenex = FoundGenex::BuildInterface;
+      pos += cmStrLen("$<BUILD_INTERFACE:");
+    } else if (pos == iPos) {
+      foundGenex = FoundGenex::InstallInterface;
+      pos += cmStrLen("$<INSTALL_INTERFACE:");
+    } else if (pos == lPos) {
+      foundGenex = FoundGenex::BuildLocalInterface;
+      pos += cmStrLen("$<BUILD_LOCAL_INTERFACE:");
+    } else {
+      assert(false && "Invalid position found");
+    }
     nestingLevel = 1;
     const char* c = input.c_str() + pos;
     const char* const cStart = c;
@@ -258,10 +269,10 @@
           continue;
         }
         if (context == cmGeneratorExpression::BuildInterface &&
-            !gotInstallInterface) {
+            foundGenex == FoundGenex::BuildInterface) {
           result += input.substr(pos, c - cStart);
         } else if (context == cmGeneratorExpression::InstallInterface &&
-                   gotInstallInterface) {
+                   foundGenex == FoundGenex::InstallInterface) {
           const std::string content = input.substr(pos, c - cStart);
           if (resolveRelative) {
             prefixItems(content, result, "${_IMPORT_PREFIX}/");
@@ -274,9 +285,18 @@
     }
     const std::string::size_type traversed = (c - cStart) + 1;
     if (!*c) {
-      result += std::string(gotInstallInterface ? "$<INSTALL_INTERFACE:"
-                                                : "$<BUILD_INTERFACE:") +
-        input.substr(pos, traversed);
+      auto remaining = input.substr(pos, traversed);
+      switch (foundGenex) {
+        case FoundGenex::BuildInterface:
+          result = cmStrCat(result, "$<BUILD_INTERFACE:", remaining);
+          break;
+        case FoundGenex::InstallInterface:
+          result = cmStrCat(result, "$<INSTALL_INTERFACE:", remaining);
+          break;
+        case FoundGenex::BuildLocalInterface:
+          result = cmStrCat(result, "$<BUILD_LOCAL_INTERFACE:", remaining);
+          break;
+      }
     }
     pos += traversed;
     lastPos = pos;
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index 562c31e..c14012e 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -114,6 +114,8 @@
 
 static const struct ZeroNode installInterfaceNode;
 
+static const struct OneNode buildLocalInterfaceNode;
+
 struct BooleanOpNode : public cmGeneratorExpressionNode
 {
   BooleanOpNode(const char* op_, const char* successVal_,
@@ -1970,7 +1972,10 @@
         }
         return std::string();
       }
-      target = context->LG->FindGeneratorTargetToUse(targetName);
+      cmLocalGenerator const* lg = context->CurrentTarget
+        ? context->CurrentTarget->GetLocalGenerator()
+        : context->LG;
+      target = lg->FindGeneratorTargetToUse(targetName);
 
       if (!target) {
         std::ostringstream e;
@@ -3320,6 +3325,7 @@
     { "GENEX_EVAL", &genexEvalNode },
     { "BUILD_INTERFACE", &buildInterfaceNode },
     { "INSTALL_INTERFACE", &installInterfaceNode },
+    { "BUILD_LOCAL_INTERFACE", &buildLocalInterfaceNode },
     { "INSTALL_PREFIX", &installPrefixNode },
     { "JOIN", &joinNode },
     { "LINK_ONLY", &linkOnlyNode },
diff --git a/Source/cmGlobalBorlandMakefileGenerator.cxx b/Source/cmGlobalBorlandMakefileGenerator.cxx
index 776ee40..2fd7f8a 100644
--- a/Source/cmGlobalBorlandMakefileGenerator.cxx
+++ b/Source/cmGlobalBorlandMakefileGenerator.cxx
@@ -60,11 +60,10 @@
   return std::unique_ptr<cmLocalGenerator>(std::move(lg));
 }
 
-void cmGlobalBorlandMakefileGenerator::GetDocumentation(
-  cmDocumentationEntry& entry)
+cmDocumentationEntry cmGlobalBorlandMakefileGenerator::GetDocumentation()
 {
-  entry.Name = cmGlobalBorlandMakefileGenerator::GetActualName();
-  entry.Brief = "Generates Borland makefiles.";
+  return { cmGlobalBorlandMakefileGenerator::GetActualName(),
+           "Generates Borland makefiles." };
 }
 
 std::vector<cmGlobalGenerator::GeneratedMakeCommand>
diff --git a/Source/cmGlobalBorlandMakefileGenerator.h b/Source/cmGlobalBorlandMakefileGenerator.h
index a280b81..049d6ba 100644
--- a/Source/cmGlobalBorlandMakefileGenerator.h
+++ b/Source/cmGlobalBorlandMakefileGenerator.h
@@ -13,7 +13,6 @@
 class cmLocalGenerator;
 class cmMakefile;
 class cmake;
-struct cmDocumentationEntry;
 
 /** \class cmGlobalBorlandMakefileGenerator
  * \brief Write a Borland makefiles.
@@ -38,7 +37,7 @@
   static std::string GetActualName() { return "Borland Makefiles"; }
 
   /** Get the documentation entry for this generator.  */
-  static void GetDocumentation(cmDocumentationEntry& entry);
+  static cmDocumentationEntry GetDocumentation();
 
   //! Create a local generator appropriate to this Global Generator
   std::unique_ptr<cmLocalGenerator> CreateLocalGenerator(
diff --git a/Source/cmGlobalGeneratorFactory.h b/Source/cmGlobalGeneratorFactory.h
index d6ababb..a935079 100644
--- a/Source/cmGlobalGeneratorFactory.h
+++ b/Source/cmGlobalGeneratorFactory.h
@@ -4,6 +4,10 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include "cmDocumentationEntry.h" // IWYU pragma: export
+
+// TODO The following headers are parts of the `cmGlobalGeneratorFactory`
+// public API, so could be defined as export to IWYU
 #include <string>
 #include <vector>
 
@@ -11,7 +15,6 @@
 
 class cmGlobalGenerator;
 class cmake;
-struct cmDocumentationEntry;
 
 /** \class cmGlobalGeneratorFactory
  * \brief Responable for creating cmGlobalGenerator instances
@@ -28,7 +31,7 @@
     const std::string& n, bool allowArch, cmake* cm) const = 0;
 
   /** Get the documentation entry for this factory */
-  virtual void GetDocumentation(cmDocumentationEntry& entry) const = 0;
+  virtual cmDocumentationEntry GetDocumentation() const = 0;
 
   /** Get the names of the current registered generators */
   virtual std::vector<std::string> GetGeneratorNames() const = 0;
@@ -47,7 +50,7 @@
   virtual std::string GetDefaultPlatformName() const = 0;
 };
 
-template <class T>
+template <typename T>
 class cmGlobalGeneratorSimpleFactory : public cmGlobalGeneratorFactory
 {
 public:
@@ -62,21 +65,19 @@
   }
 
   /** Get the documentation entry for this factory */
-  void GetDocumentation(cmDocumentationEntry& entry) const override
+  cmDocumentationEntry GetDocumentation() const override
   {
-    T::GetDocumentation(entry);
+    return T::GetDocumentation();
   }
 
   /** Get the names of the current registered generators */
   std::vector<std::string> GetGeneratorNames() const override
   {
-    std::vector<std::string> names;
-    names.push_back(T::GetActualName());
-    return names;
+    return { T::GetActualName() };
   }
   std::vector<std::string> GetGeneratorNamesWithPlatform() const override
   {
-    return std::vector<std::string>();
+    return {};
   }
 
   /** Determine whether or not this generator supports toolsets */
@@ -89,8 +90,8 @@
   std::vector<std::string> GetKnownPlatforms() const override
   {
     // default is no platform supported
-    return std::vector<std::string>();
+    return {};
   }
 
-  std::string GetDefaultPlatformName() const override { return std::string(); }
+  std::string GetDefaultPlatformName() const override { return {}; }
 };
diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx
index 9c334a5..18c48d7 100644
--- a/Source/cmGlobalGhsMultiGenerator.cxx
+++ b/Source/cmGlobalGhsMultiGenerator.cxx
@@ -15,7 +15,6 @@
 
 #include "cmCustomCommand.h"
 #include "cmCustomCommandLines.h"
-#include "cmDocumentationEntry.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorTarget.h"
 #include "cmGhsMultiGpj.h"
@@ -58,11 +57,12 @@
     cm::make_unique<cmLocalGhsMultiGenerator>(this, mf));
 }
 
-void cmGlobalGhsMultiGenerator::GetDocumentation(cmDocumentationEntry& entry)
+cmDocumentationEntry cmGlobalGhsMultiGenerator::GetDocumentation()
 {
-  entry.Name = GetActualName();
-  entry.Brief =
-    "Generates Green Hills MULTI files (experimental, work-in-progress).";
+  return {
+    GetActualName(),
+    "Generates Green Hills MULTI files (experimental, work-in-progress)."
+  };
 }
 
 void cmGlobalGhsMultiGenerator::ComputeTargetObjectDirectory(
diff --git a/Source/cmGlobalGhsMultiGenerator.h b/Source/cmGlobalGhsMultiGenerator.h
index aa68d3b..4a79610 100644
--- a/Source/cmGlobalGhsMultiGenerator.h
+++ b/Source/cmGlobalGhsMultiGenerator.h
@@ -18,7 +18,6 @@
 class cmLocalGenerator;
 class cmMakefile;
 class cmake;
-struct cmDocumentationEntry;
 
 class cmGlobalGhsMultiGenerator : public cmGlobalGenerator
 {
@@ -46,7 +45,7 @@
   std::string GetName() const override { return GetActualName(); }
 
   /// Overloaded methods. @see cmGlobalGenerator::GetDocumentation()
-  static void GetDocumentation(cmDocumentationEntry& entry);
+  static cmDocumentationEntry GetDocumentation();
 
   /**
    * Utilized by the generator factory to determine if this generator
diff --git a/Source/cmGlobalJOMMakefileGenerator.cxx b/Source/cmGlobalJOMMakefileGenerator.cxx
index 1a625cc..f1d4d09 100644
--- a/Source/cmGlobalJOMMakefileGenerator.cxx
+++ b/Source/cmGlobalJOMMakefileGenerator.cxx
@@ -6,7 +6,6 @@
 
 #include <cmext/algorithm>
 
-#include "cmDocumentationEntry.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmState.h"
@@ -36,11 +35,10 @@
   this->cmGlobalUnixMakefileGenerator3::EnableLanguage(l, mf, optional);
 }
 
-void cmGlobalJOMMakefileGenerator::GetDocumentation(
-  cmDocumentationEntry& entry)
+cmDocumentationEntry cmGlobalJOMMakefileGenerator::GetDocumentation()
 {
-  entry.Name = cmGlobalJOMMakefileGenerator::GetActualName();
-  entry.Brief = "Generates JOM makefiles.";
+  return { cmGlobalJOMMakefileGenerator::GetActualName(),
+           "Generates JOM makefiles." };
 }
 
 void cmGlobalJOMMakefileGenerator::PrintCompilerAdvice(std::ostream& os,
diff --git a/Source/cmGlobalJOMMakefileGenerator.h b/Source/cmGlobalJOMMakefileGenerator.h
index 332d1cf..5e74875 100644
--- a/Source/cmGlobalJOMMakefileGenerator.h
+++ b/Source/cmGlobalJOMMakefileGenerator.h
@@ -13,7 +13,6 @@
 
 class cmMakefile;
 class cmake;
-struct cmDocumentationEntry;
 
 /** \class cmGlobalJOMMakefileGenerator
  * \brief Write a JOM makefiles.
@@ -39,7 +38,7 @@
   static std::string GetActualName() { return "NMake Makefiles JOM"; }
 
   /** Get the documentation entry for this generator.  */
-  static void GetDocumentation(cmDocumentationEntry& entry);
+  static cmDocumentationEntry GetDocumentation();
 
   /**
    * Try to determine system information such as shared library
diff --git a/Source/cmGlobalMSYSMakefileGenerator.cxx b/Source/cmGlobalMSYSMakefileGenerator.cxx
index d4ff1e0..e543aea 100644
--- a/Source/cmGlobalMSYSMakefileGenerator.cxx
+++ b/Source/cmGlobalMSYSMakefileGenerator.cxx
@@ -4,7 +4,6 @@
 
 #include "cmsys/FStream.hxx"
 
-#include "cmDocumentationEntry.h"
 #include "cmMakefile.h"
 #include "cmState.h"
 #include "cmStringAlgorithms.h"
@@ -53,9 +52,8 @@
   }
 }
 
-void cmGlobalMSYSMakefileGenerator::GetDocumentation(
-  cmDocumentationEntry& entry)
+cmDocumentationEntry cmGlobalMSYSMakefileGenerator::GetDocumentation()
 {
-  entry.Name = cmGlobalMSYSMakefileGenerator::GetActualName();
-  entry.Brief = "Generates MSYS makefiles.";
+  return { cmGlobalMSYSMakefileGenerator::GetActualName(),
+           "Generates MSYS makefiles." };
 }
diff --git a/Source/cmGlobalMSYSMakefileGenerator.h b/Source/cmGlobalMSYSMakefileGenerator.h
index 586487f..a8516a4 100644
--- a/Source/cmGlobalMSYSMakefileGenerator.h
+++ b/Source/cmGlobalMSYSMakefileGenerator.h
@@ -11,7 +11,6 @@
 
 class cmMakefile;
 class cmake;
-struct cmDocumentationEntry;
 
 /** \class cmGlobalMSYSMakefileGenerator
  * \brief Write a NMake makefiles.
@@ -36,7 +35,7 @@
   static std::string GetActualName() { return "MSYS Makefiles"; }
 
   /** Get the documentation entry for this generator.  */
-  static void GetDocumentation(cmDocumentationEntry& entry);
+  static cmDocumentationEntry GetDocumentation();
 
   /**
    * Try to determine system information such as shared library
diff --git a/Source/cmGlobalMinGWMakefileGenerator.cxx b/Source/cmGlobalMinGWMakefileGenerator.cxx
index 5a7edae..a0a52d3 100644
--- a/Source/cmGlobalMinGWMakefileGenerator.cxx
+++ b/Source/cmGlobalMinGWMakefileGenerator.cxx
@@ -2,7 +2,6 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGlobalMinGWMakefileGenerator.h"
 
-#include "cmDocumentationEntry.h"
 #include "cmMakefile.h"
 #include "cmState.h"
 #include "cmSystemTools.h"
@@ -19,9 +18,8 @@
   cm->GetState()->SetMinGWMake(true);
 }
 
-void cmGlobalMinGWMakefileGenerator::GetDocumentation(
-  cmDocumentationEntry& entry)
+cmDocumentationEntry cmGlobalMinGWMakefileGenerator::GetDocumentation()
 {
-  entry.Name = cmGlobalMinGWMakefileGenerator::GetActualName();
-  entry.Brief = "Generates a make file for use with mingw32-make.";
+  return { cmGlobalMinGWMakefileGenerator::GetActualName(),
+           "Generates a make file for use with mingw32-make." };
 }
diff --git a/Source/cmGlobalMinGWMakefileGenerator.h b/Source/cmGlobalMinGWMakefileGenerator.h
index 92d495c..b39db03 100644
--- a/Source/cmGlobalMinGWMakefileGenerator.h
+++ b/Source/cmGlobalMinGWMakefileGenerator.h
@@ -11,7 +11,6 @@
 
 class cmMakefile;
 class cmake;
-struct cmDocumentationEntry;
 
 /** \class cmGlobalMinGWMakefileGenerator
  * \brief Write a NMake makefiles.
@@ -35,5 +34,5 @@
   static std::string GetActualName() { return "MinGW Makefiles"; }
 
   /** Get the documentation entry for this generator.  */
-  static void GetDocumentation(cmDocumentationEntry& entry);
+  static cmDocumentationEntry GetDocumentation();
 };
diff --git a/Source/cmGlobalNMakeMakefileGenerator.cxx b/Source/cmGlobalNMakeMakefileGenerator.cxx
index eabacf6..cb53850 100644
--- a/Source/cmGlobalNMakeMakefileGenerator.cxx
+++ b/Source/cmGlobalNMakeMakefileGenerator.cxx
@@ -8,7 +8,6 @@
 
 #include "cmsys/RegularExpression.hxx"
 
-#include "cmDocumentationEntry.h"
 #include "cmDuration.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
@@ -80,11 +79,10 @@
     cmSystemTools::OP_LESS, this->NMakeVersion, "9");
 }
 
-void cmGlobalNMakeMakefileGenerator::GetDocumentation(
-  cmDocumentationEntry& entry)
+cmDocumentationEntry cmGlobalNMakeMakefileGenerator::GetDocumentation()
 {
-  entry.Name = cmGlobalNMakeMakefileGenerator::GetActualName();
-  entry.Brief = "Generates NMake makefiles.";
+  return { cmGlobalNMakeMakefileGenerator::GetActualName(),
+           "Generates NMake makefiles." };
 }
 
 void cmGlobalNMakeMakefileGenerator::PrintCompilerAdvice(
@@ -128,12 +126,8 @@
   if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) {
     // nmake does not support parallel build level
     // see https://msdn.microsoft.com/en-us/library/afyyse50.aspx
-
-    /* clang-format off */
-    os <<
-      "Warning: NMake does not support parallel builds. "
-      "Ignoring parallel build command line option.\n";
-    /* clang-format on */
+    os << "Warning: NMake does not support parallel builds. "
+          "Ignoring parallel build command line option.\n";
   }
 
   this->cmGlobalUnixMakefileGenerator3::PrintBuildCommandAdvice(
diff --git a/Source/cmGlobalNMakeMakefileGenerator.h b/Source/cmGlobalNMakeMakefileGenerator.h
index b3574eb..436ebca 100644
--- a/Source/cmGlobalNMakeMakefileGenerator.h
+++ b/Source/cmGlobalNMakeMakefileGenerator.h
@@ -15,7 +15,6 @@
 
 class cmMakefile;
 class cmake;
-struct cmDocumentationEntry;
 
 /** \class cmGlobalNMakeMakefileGenerator
  * \brief Write a NMake makefiles.
@@ -45,7 +44,7 @@
   }
 
   /** Get the documentation entry for this generator.  */
-  static void GetDocumentation(cmDocumentationEntry& entry);
+  static cmDocumentationEntry GetDocumentation();
 
   /**
    * Try to determine system information such as shared library
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index 395372b..aa1ded0 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -25,7 +25,6 @@
 #include "cmsys/FStream.hxx"
 
 #include "cmCxxModuleMapper.h"
-#include "cmDocumentationEntry.h"
 #include "cmFileSet.h"
 #include "cmFortranParser.h"
 #include "cmGeneratedFileStream.h"
@@ -554,10 +553,10 @@
   return this->NinjaExpectedEncoding;
 }
 
-void cmGlobalNinjaGenerator::GetDocumentation(cmDocumentationEntry& entry)
+cmDocumentationEntry cmGlobalNinjaGenerator::GetDocumentation()
 {
-  entry.Name = cmGlobalNinjaGenerator::GetActualName();
-  entry.Brief = "Generates build.ninja files.";
+  return { cmGlobalNinjaGenerator::GetActualName(),
+           "Generates build.ninja files." };
 }
 
 // Implemented in all cmGlobaleGenerator sub-classes.
@@ -3211,10 +3210,10 @@
   cm->GetState()->SetNinjaMulti(true);
 }
 
-void cmGlobalNinjaMultiGenerator::GetDocumentation(cmDocumentationEntry& entry)
+cmDocumentationEntry cmGlobalNinjaMultiGenerator::GetDocumentation()
 {
-  entry.Name = cmGlobalNinjaMultiGenerator::GetActualName();
-  entry.Brief = "Generates build-<Config>.ninja files.";
+  return { cmGlobalNinjaMultiGenerator::GetActualName(),
+           "Generates build-<Config>.ninja files." };
 }
 
 std::string cmGlobalNinjaMultiGenerator::ExpandCFGIntDir(
diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h
index dac1c52..1628349 100644
--- a/Source/cmGlobalNinjaGenerator.h
+++ b/Source/cmGlobalNinjaGenerator.h
@@ -35,7 +35,6 @@
 class cmOutputConverter;
 class cmStateDirectory;
 class cmake;
-struct cmDocumentationEntry;
 
 /**
  * \class cmGlobalNinjaGenerator
@@ -193,7 +192,7 @@
   /** Get encoding used by generator for ninja files */
   codecvt::Encoding GetMakefileEncoding() const override;
 
-  static void GetDocumentation(cmDocumentationEntry& entry);
+  static cmDocumentationEntry GetDocumentation();
 
   void EnableLanguage(std::vector<std::string> const& languages,
                       cmMakefile* mf, bool optional) override;
@@ -656,7 +655,7 @@
       new cmGlobalGeneratorSimpleFactory<cmGlobalNinjaMultiGenerator>());
   }
 
-  static void GetDocumentation(cmDocumentationEntry& entry);
+  static cmDocumentationEntry GetDocumentation();
 
   std::string GetName() const override
   {
diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx
index bf9e40e..70a9d3e 100644
--- a/Source/cmGlobalUnixMakefileGenerator3.cxx
+++ b/Source/cmGlobalUnixMakefileGenerator3.cxx
@@ -11,7 +11,6 @@
 #include <cmext/algorithm>
 #include <cmext/memory>
 
-#include "cmDocumentationEntry.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
@@ -71,11 +70,10 @@
     cm::make_unique<cmLocalUnixMakefileGenerator3>(this, mf));
 }
 
-void cmGlobalUnixMakefileGenerator3::GetDocumentation(
-  cmDocumentationEntry& entry)
+cmDocumentationEntry cmGlobalUnixMakefileGenerator3::GetDocumentation()
 {
-  entry.Name = cmGlobalUnixMakefileGenerator3::GetActualName();
-  entry.Brief = "Generates standard UNIX makefiles.";
+  return { cmGlobalUnixMakefileGenerator3::GetActualName(),
+           "Generates standard UNIX makefiles." };
 }
 
 void cmGlobalUnixMakefileGenerator3::ComputeTargetObjectDirectory(
diff --git a/Source/cmGlobalUnixMakefileGenerator3.h b/Source/cmGlobalUnixMakefileGenerator3.h
index 92e567a..214ba2a 100644
--- a/Source/cmGlobalUnixMakefileGenerator3.h
+++ b/Source/cmGlobalUnixMakefileGenerator3.h
@@ -24,7 +24,6 @@
 class cmMakefile;
 class cmMakefileTargetGenerator;
 class cmake;
-struct cmDocumentationEntry;
 
 /** \class cmGlobalUnixMakefileGenerator3
  * \brief Write a Unix makefiles.
@@ -101,7 +100,7 @@
   bool SupportsCustomCommandDepfile() const override { return true; }
 
   /** Get the documentation entry for this generator.  */
-  static void GetDocumentation(cmDocumentationEntry& entry);
+  static cmDocumentationEntry GetDocumentation();
 
   std::unique_ptr<cmLocalGenerator> CreateLocalGenerator(
     cmMakefile* mf) override;
diff --git a/Source/cmGlobalVisualStudio11Generator.cxx b/Source/cmGlobalVisualStudio11Generator.cxx
index c53ddf5..75e3df8 100644
--- a/Source/cmGlobalVisualStudio11Generator.cxx
+++ b/Source/cmGlobalVisualStudio11Generator.cxx
@@ -7,7 +7,6 @@
 #include <utility>
 #include <vector>
 
-#include "cmDocumentationEntry.h"
 #include "cmGlobalGenerator.h"
 #include "cmGlobalGeneratorFactory.h"
 #include "cmGlobalVisualStudioGenerator.h"
@@ -74,11 +73,11 @@
     return std::unique_ptr<cmGlobalGenerator>(std::move(ret));
   }
 
-  void GetDocumentation(cmDocumentationEntry& entry) const override
+  cmDocumentationEntry GetDocumentation() const override
   {
-    entry.Name = std::string(vs11generatorName) + " [arch]";
-    entry.Brief = "Deprecated.  Generates Visual Studio 2012 project files.  "
-                  "Optional [arch] can be \"Win64\" or \"ARM\".";
+    return { std::string(vs11generatorName) + " [arch]",
+             "Deprecated.  Generates Visual Studio 2012 project files.  "
+             "Optional [arch] can be \"Win64\" or \"ARM\"." };
   }
 
   std::vector<std::string> GetGeneratorNames() const override
diff --git a/Source/cmGlobalVisualStudio12Generator.cxx b/Source/cmGlobalVisualStudio12Generator.cxx
index 600ee0a..7e56a78 100644
--- a/Source/cmGlobalVisualStudio12Generator.cxx
+++ b/Source/cmGlobalVisualStudio12Generator.cxx
@@ -6,7 +6,6 @@
 #include <sstream>
 #include <vector>
 
-#include "cmDocumentationEntry.h"
 #include "cmGlobalGenerator.h"
 #include "cmGlobalGeneratorFactory.h"
 #include "cmGlobalVisualStudioGenerator.h"
@@ -62,11 +61,11 @@
     return std::unique_ptr<cmGlobalGenerator>();
   }
 
-  void GetDocumentation(cmDocumentationEntry& entry) const override
+  cmDocumentationEntry GetDocumentation() const override
   {
-    entry.Name = std::string(vs12generatorName) + " [arch]";
-    entry.Brief = "Generates Visual Studio 2013 project files.  "
-                  "Optional [arch] can be \"Win64\" or \"ARM\".";
+    return { std::string(vs12generatorName) + " [arch]",
+             "Generates Visual Studio 2013 project files.  "
+             "Optional [arch] can be \"Win64\" or \"ARM\"." };
   }
 
   std::vector<std::string> GetGeneratorNames() const override
diff --git a/Source/cmGlobalVisualStudio14Generator.cxx b/Source/cmGlobalVisualStudio14Generator.cxx
index 9f94cca..0e05083 100644
--- a/Source/cmGlobalVisualStudio14Generator.cxx
+++ b/Source/cmGlobalVisualStudio14Generator.cxx
@@ -7,7 +7,6 @@
 
 #include <cm/vector>
 
-#include "cmDocumentationEntry.h"
 #include "cmGlobalGenerator.h"
 #include "cmGlobalGeneratorFactory.h"
 #include "cmGlobalVisualStudioGenerator.h"
@@ -64,11 +63,11 @@
     return std::unique_ptr<cmGlobalGenerator>();
   }
 
-  void GetDocumentation(cmDocumentationEntry& entry) const override
+  cmDocumentationEntry GetDocumentation() const override
   {
-    entry.Name = std::string(vs14generatorName) + " [arch]";
-    entry.Brief = "Generates Visual Studio 2015 project files.  "
-                  "Optional [arch] can be \"Win64\" or \"ARM\".";
+    return { std::string(vs14generatorName) + " [arch]",
+             "Generates Visual Studio 2015 project files.  "
+             "Optional [arch] can be \"Win64\" or \"ARM\"." };
   }
 
   std::vector<std::string> GetGeneratorNames() const override
diff --git a/Source/cmGlobalVisualStudio9Generator.cxx b/Source/cmGlobalVisualStudio9Generator.cxx
index e03e665..cd0b114 100644
--- a/Source/cmGlobalVisualStudio9Generator.cxx
+++ b/Source/cmGlobalVisualStudio9Generator.cxx
@@ -6,7 +6,6 @@
 #include <utility>
 #include <vector>
 
-#include "cmDocumentationEntry.h"
 #include "cmGlobalGenerator.h"
 #include "cmGlobalGeneratorFactory.h"
 #include "cmGlobalVisualStudioGenerator.h"
@@ -62,11 +61,11 @@
     return std::unique_ptr<cmGlobalGenerator>(std::move(ret));
   }
 
-  void GetDocumentation(cmDocumentationEntry& entry) const override
+  cmDocumentationEntry GetDocumentation() const override
   {
-    entry.Name = std::string(vs9generatorName) + " [arch]";
-    entry.Brief = "Generates Visual Studio 2008 project files.  "
-                  "Optional [arch] can be \"Win64\" or \"IA64\".";
+    return { std::string(vs9generatorName) + " [arch]",
+             "Generates Visual Studio 2008 project files.  "
+             "Optional [arch] can be \"Win64\" or \"IA64\"." };
   }
 
   std::vector<std::string> GetGeneratorNames() const override
diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
index be318c1..ee24f05 100644
--- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx
+++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
@@ -14,7 +14,6 @@
 #include "cmsys/Glob.hxx"
 #include "cmsys/RegularExpression.hxx"
 
-#include "cmDocumentationEntry.h"
 #include "cmGlobalGenerator.h"
 #include "cmGlobalGeneratorFactory.h"
 #include "cmMakefile.h"
@@ -255,11 +254,11 @@
     return std::unique_ptr<cmGlobalGenerator>();
   }
 
-  void GetDocumentation(cmDocumentationEntry& entry) const override
+  cmDocumentationEntry GetDocumentation() const override
   {
-    entry.Name = std::string(vs15generatorName) + " [arch]";
-    entry.Brief = "Generates Visual Studio 2017 project files.  "
-                  "Optional [arch] can be \"Win64\" or \"ARM\".";
+    return { std::string(vs15generatorName) + " [arch]",
+             "Generates Visual Studio 2017 project files.  "
+             "Optional [arch] can be \"Win64\" or \"ARM\"." };
   }
 
   std::vector<std::string> GetGeneratorNames() const override
@@ -351,11 +350,11 @@
     return std::unique_ptr<cmGlobalGenerator>();
   }
 
-  void GetDocumentation(cmDocumentationEntry& entry) const override
+  cmDocumentationEntry GetDocumentation() const override
   {
-    entry.Name = std::string(vs16generatorName);
-    entry.Brief = "Generates Visual Studio 2019 project files.  "
-                  "Use -A option to specify architecture.";
+    return { std::string(vs16generatorName),
+             "Generates Visual Studio 2019 project files.  "
+             "Use -A option to specify architecture." };
   }
 
   std::vector<std::string> GetGeneratorNames() const override
@@ -416,11 +415,11 @@
     return std::unique_ptr<cmGlobalGenerator>();
   }
 
-  void GetDocumentation(cmDocumentationEntry& entry) const override
+  cmDocumentationEntry GetDocumentation() const override
   {
-    entry.Name = std::string(vs17generatorName);
-    entry.Brief = "Generates Visual Studio 2022 project files.  "
-                  "Use -A option to specify architecture.";
+    return { std::string(vs17generatorName),
+             "Generates Visual Studio 2022 project files.  "
+             "Use -A option to specify architecture." };
   }
 
   std::vector<std::string> GetGeneratorNames() const override
diff --git a/Source/cmGlobalWatcomWMakeGenerator.cxx b/Source/cmGlobalWatcomWMakeGenerator.cxx
index fb2a8b6..ed44e6b 100644
--- a/Source/cmGlobalWatcomWMakeGenerator.cxx
+++ b/Source/cmGlobalWatcomWMakeGenerator.cxx
@@ -4,7 +4,6 @@
 
 #include <ostream>
 
-#include "cmDocumentationEntry.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmState.h"
@@ -54,11 +53,10 @@
   return this->cmGlobalUnixMakefileGenerator3::SetSystemName(s, mf);
 }
 
-void cmGlobalWatcomWMakeGenerator::GetDocumentation(
-  cmDocumentationEntry& entry)
+cmDocumentationEntry cmGlobalWatcomWMakeGenerator::GetDocumentation()
 {
-  entry.Name = cmGlobalWatcomWMakeGenerator::GetActualName();
-  entry.Brief = "Generates Watcom WMake makefiles.";
+  return { cmGlobalWatcomWMakeGenerator::GetActualName(),
+           "Generates Watcom WMake makefiles." };
 }
 
 std::vector<cmGlobalGenerator::GeneratedMakeCommand>
diff --git a/Source/cmGlobalWatcomWMakeGenerator.h b/Source/cmGlobalWatcomWMakeGenerator.h
index eb93934..5579120 100644
--- a/Source/cmGlobalWatcomWMakeGenerator.h
+++ b/Source/cmGlobalWatcomWMakeGenerator.h
@@ -15,7 +15,6 @@
 
 class cmMakefile;
 class cmake;
-struct cmDocumentationEntry;
 
 /** \class cmGlobalWatcomWMakeGenerator
  * \brief Write a NMake makefiles.
@@ -39,7 +38,7 @@
   static std::string GetActualName() { return "Watcom WMake"; }
 
   /** Get the documentation entry for this generator.  */
-  static void GetDocumentation(cmDocumentationEntry& entry);
+  static cmDocumentationEntry GetDocumentation();
 
   /** Tell the generator about the target system.  */
   bool SetSystemName(std::string const& s, cmMakefile* mf) override;
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index 62e5a1e..0e94de5 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -24,7 +24,6 @@
 #include "cmCustomCommandGenerator.h"
 #include "cmCustomCommandLines.h"
 #include "cmCustomCommandTypes.h"
-#include "cmDocumentationEntry.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
@@ -149,9 +148,9 @@
   std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
     const std::string& name, bool allowArch, cmake* cm) const override;
 
-  void GetDocumentation(cmDocumentationEntry& entry) const override
+  cmDocumentationEntry GetDocumentation() const override
   {
-    cmGlobalXCodeGenerator::GetDocumentation(entry);
+    return cmGlobalXCodeGenerator::GetDocumentation();
   }
 
   std::vector<std::string> GetGeneratorNames() const override
@@ -4864,10 +4863,10 @@
   return tmp;
 }
 
-void cmGlobalXCodeGenerator::GetDocumentation(cmDocumentationEntry& entry)
+cmDocumentationEntry cmGlobalXCodeGenerator::GetDocumentation()
 {
-  entry.Name = cmGlobalXCodeGenerator::GetActualName();
-  entry.Brief = "Generate Xcode project files.";
+  return { cmGlobalXCodeGenerator::GetActualName(),
+           "Generate Xcode project files." };
 }
 
 std::string cmGlobalXCodeGenerator::ConvertToRelativeForMake(
diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h
index 9ae75fb..856852b 100644
--- a/Source/cmGlobalXCodeGenerator.h
+++ b/Source/cmGlobalXCodeGenerator.h
@@ -14,6 +14,7 @@
 #include <cm/optional>
 #include <cm/string_view>
 
+#include "cmDocumentationEntry.h"
 #include "cmGlobalGenerator.h"
 #include "cmTransformDepfile.h"
 #include "cmValue.h"
@@ -28,7 +29,6 @@
 class cmSourceFile;
 class cmSourceGroup;
 class cmake;
-struct cmDocumentationEntry;
 
 /** \class cmGlobalXCodeGenerator
  * \brief Write a Unix makefiles.
@@ -54,7 +54,7 @@
   static std::string GetActualName() { return "Xcode"; }
 
   /** Get the documentation entry for this generator.  */
-  static void GetDocumentation(cmDocumentationEntry& entry);
+  static cmDocumentationEntry GetDocumentation();
 
   //! Create a local generator appropriate to this Global Generator
   std::unique_ptr<cmLocalGenerator> CreateLocalGenerator(
diff --git a/Source/cmMessenger.cxx b/Source/cmMessenger.cxx
index 333003b..ff513be 100644
--- a/Source/cmMessenger.cxx
+++ b/Source/cmMessenger.cxx
@@ -107,8 +107,8 @@
 {
   msg << ":\n";
   cmDocumentationFormatter formatter;
-  formatter.SetIndent("  ");
-  formatter.PrintFormatted(msg, text.c_str());
+  formatter.SetIndent(2u);
+  formatter.PrintFormatted(msg, text);
 }
 
 static void displayMessage(MessageType t, std::ostringstream& msg)
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
index 895a4c3..21409d7 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -1112,7 +1112,9 @@
           this->GetObjectFilePath(source, config));
       }
     }
-    linkBuild.Outputs.push_back(vars["SWIFT_MODULE"]);
+    if (targetType != cmStateEnums::EXECUTABLE) {
+      linkBuild.Outputs.push_back(vars["SWIFT_MODULE"]);
+    }
   } else {
     linkBuild.ExplicitDeps = this->GetObjects(config);
   }
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 36c0089..befcb55 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -40,7 +40,6 @@
 #include "cmCommands.h"
 #include "cmDocumentation.h"
 #include "cmDocumentationEntry.h"
-#include "cmDocumentationFormatter.h"
 #include "cmDuration.h"
 #include "cmExternalMakefileProjectGenerator.h"
 #include "cmFileTimeCache.h"
@@ -150,21 +149,136 @@
 using CommandArgument =
   cmCommandLineArgument<bool(std::string const& value, cmake* state)>;
 
-} // namespace
-
-static bool cmakeCheckStampFile(const std::string& stampName);
-static bool cmakeCheckStampList(const std::string& stampList);
-
 #ifndef CMAKE_BOOTSTRAP
-static void cmWarnUnusedCliWarning(const std::string& variable, int /*unused*/,
-                                   void* ctx, const char* /*unused*/,
-                                   const cmMakefile* /*unused*/)
+void cmWarnUnusedCliWarning(const std::string& variable, int /*unused*/,
+                            void* ctx, const char* /*unused*/,
+                            const cmMakefile* /*unused*/)
 {
   cmake* cm = reinterpret_cast<cmake*>(ctx);
   cm->MarkCliAsUsed(variable);
 }
 #endif
 
+bool cmakeCheckStampFile(const std::string& stampName)
+{
+  // The stamp file does not exist.  Use the stamp dependencies to
+  // determine whether it is really out of date.  This works in
+  // conjunction with cmLocalVisualStudio7Generator to avoid
+  // repeatedly re-running CMake when the user rebuilds the entire
+  // solution.
+  std::string stampDepends = cmStrCat(stampName, ".depend");
+#if defined(_WIN32) || defined(__CYGWIN__)
+  cmsys::ifstream fin(stampDepends.c_str(), std::ios::in | std::ios::binary);
+#else
+  cmsys::ifstream fin(stampDepends.c_str());
+#endif
+  if (!fin) {
+    // The stamp dependencies file cannot be read.  Just assume the
+    // build system is really out of date.
+    std::cout << "CMake is re-running because " << stampName
+              << " dependency file is missing.\n";
+    return false;
+  }
+
+  // Compare the stamp dependencies against the dependency file itself.
+  {
+    cmFileTimeCache ftc;
+    std::string dep;
+    while (cmSystemTools::GetLineFromStream(fin, dep)) {
+      int result;
+      if (!dep.empty() && dep[0] != '#' &&
+          (!ftc.Compare(stampDepends, dep, &result) || result < 0)) {
+        // The stamp depends file is older than this dependency.  The
+        // build system is really out of date.
+        /* clang-format off */
+        std::cout << "CMake is re-running because " << stampName
+                  << " is out-of-date.\n"
+                     "  the file '" << dep << "'\n"
+                     "  is newer than '" << stampDepends << "'\n"
+                     "  result='" << result << "'\n";
+        /* clang-format on */
+        return false;
+      }
+    }
+  }
+
+  // The build system is up to date.  The stamp file has been removed
+  // by the VS IDE due to a "rebuild" request.  Restore it atomically.
+  std::ostringstream stampTempStream;
+  stampTempStream << stampName << ".tmp" << cmSystemTools::RandomSeed();
+  std::string stampTemp = stampTempStream.str();
+  {
+    // TODO: Teach cmGeneratedFileStream to use a random temp file (with
+    // multiple tries in unlikely case of conflict) and use that here.
+    cmsys::ofstream stamp(stampTemp.c_str());
+    stamp << "# CMake generation timestamp file for this directory.\n";
+  }
+  std::string err;
+  if (cmSystemTools::RenameFile(stampTemp, stampName,
+                                cmSystemTools::Replace::Yes, &err) ==
+      cmSystemTools::RenameResult::Success) {
+    // CMake does not need to re-run because the stamp file is up-to-date.
+    return true;
+  }
+  cmSystemTools::RemoveFile(stampTemp);
+  cmSystemTools::Error(
+    cmStrCat("Cannot restore timestamp \"", stampName, "\": ", err));
+  return false;
+}
+
+bool cmakeCheckStampList(const std::string& stampList)
+{
+  // If the stamp list does not exist CMake must rerun to generate it.
+  if (!cmSystemTools::FileExists(stampList)) {
+    std::cout << "CMake is re-running because generate.stamp.list "
+                 "is missing.\n";
+    return false;
+  }
+  cmsys::ifstream fin(stampList.c_str());
+  if (!fin) {
+    std::cout << "CMake is re-running because generate.stamp.list "
+                 "could not be read.\n";
+    return false;
+  }
+
+  // Check each stamp.
+  std::string stampName;
+  while (cmSystemTools::GetLineFromStream(fin, stampName)) {
+    if (!cmakeCheckStampFile(stampName)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+} // namespace
+
+cmDocumentationEntry cmake::CMAKE_STANDARD_OPTIONS_TABLE[18] = {
+  { "-S <path-to-source>", "Explicitly specify a source directory." },
+  { "-B <path-to-build>", "Explicitly specify a build directory." },
+  { "-C <initial-cache>", "Pre-load a script to populate the cache." },
+  { "-D <var>[:<type>]=<value>", "Create or update a cmake cache entry." },
+  { "-U <globbing_expr>", "Remove matching entries from CMake cache." },
+  { "-G <generator-name>", "Specify a build system generator." },
+  { "-T <toolset-name>", "Specify toolset name if supported by generator." },
+  { "-A <platform-name>", "Specify platform name if supported by generator." },
+  { "--toolchain <file>", "Specify toolchain file [CMAKE_TOOLCHAIN_FILE]." },
+  { "--install-prefix <directory>",
+    "Specify install directory [CMAKE_INSTALL_PREFIX]." },
+  { "-Wdev", "Enable developer warnings." },
+  { "-Wno-dev", "Suppress developer warnings." },
+  { "-Werror=dev", "Make developer warnings errors." },
+  { "-Wno-error=dev", "Make developer warnings not errors." },
+  { "-Wdeprecated", "Enable deprecation warnings." },
+  { "-Wno-deprecated", "Suppress deprecation warnings." },
+  { "-Werror=deprecated",
+    "Make deprecated macro and function warnings "
+    "errors." },
+  { "-Wno-error=deprecated",
+    "Make deprecated macro and function warnings "
+    "not errors." }
+};
+
 cmake::cmake(Role role, cmState::Mode mode, cmState::ProjectKind projectKind)
   : CMakeWorkingDirectory(cmSystemTools::GetCurrentWorkingDirectory())
   , FileTimeCache(cm::make_unique<cmFileTimeCache>())
@@ -1008,7 +1122,7 @@
         std::cout << "Running with debug output on for the 'find' commands "
                      "for package(s)";
         for (auto const& v : find_pkgs) {
-          std::cout << " " << v;
+          std::cout << ' ' << v;
           state->SetDebugFindOutputPkgs(v);
         }
         std::cout << ".\n";
@@ -1021,7 +1135,7 @@
         std::vector<std::string> find_vars(cmTokenize(value, ","));
         std::cout << "Running with debug output on for the variable(s)";
         for (auto const& v : find_vars) {
-          std::cout << " " << v;
+          std::cout << ' ' << v;
           state->SetDebugFindOutputVars(v);
         }
         std::cout << ".\n";
@@ -1082,7 +1196,7 @@
                      [](std::string const&, cmake* state) -> bool {
                        std::cout
                          << "Not searching for unused variables given on the "
-                         << "command line.\n";
+                            "command line.\n";
                        state->SetWarnUnusedCli(false);
                        return true;
                      } },
@@ -1090,7 +1204,7 @@
       "--check-system-vars", CommandArgument::Values::Zero,
       [](std::string const&, cmake* state) -> bool {
         std::cout << "Also check system files when warning about unused and "
-                  << "uninitialized variables.\n";
+                     "uninitialized variables.\n";
         state->SetCheckSystemVars(true);
         return true;
       } },
@@ -1098,7 +1212,7 @@
       "--compile-no-warning-as-error", CommandArgument::Values::Zero,
       [](std::string const&, cmake* state) -> bool {
         std::cout << "Ignoring COMPILE_WARNING_AS_ERROR target property and "
-                  << "CMAKE_COMPILE_WARNING_AS_ERROR variable.\n";
+                     "CMAKE_COMPILE_WARNING_AS_ERROR variable.\n";
         state->SetIgnoreWarningAsError(true);
         return true;
       } }
@@ -1497,7 +1611,7 @@
     cmSystemTools::Error(ss.str());
     return;
   }
-  std::cout << "Trace will be written to " << file << "\n";
+  std::cout << "Trace will be written to " << file << '\n';
 }
 
 void cmake::PrintTraceFormatVersion()
@@ -2020,12 +2134,10 @@
   }
   std::vector<SaveCacheEntry> saved;
   std::ostringstream warning;
-  /* clang-format off */
   warning
     << "You have changed variables that require your cache to be deleted.\n"
-    << "Configure will be re-run and you may have to reset some variables.\n"
-    << "The following variables have changed:\n";
-  /* clang-format on */
+       "Configure will be re-run and you may have to reset some variables.\n"
+       "The following variables have changed:\n";
   for (auto i = argsSplit.begin(); i != argsSplit.end(); ++i) {
     SaveCacheEntry save;
     save.key = *i;
@@ -2033,9 +2145,9 @@
     i++;
     if (i != argsSplit.end()) {
       save.value = *i;
-      warning << *i << "\n";
+      warning << *i << '\n';
     } else {
-      warning << "\n";
+      warning << '\n';
       i -= 1;
     }
     cmValue existingValue = this->State->GetCacheEntryValue(save.key);
@@ -2437,7 +2549,7 @@
   auto gen = this->EvaluateDefaultGlobalGenerator();
 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(CMAKE_BOOT_MINGW)
   // This print could be unified for all platforms
-  std::cout << "-- Building for: " << gen->GetName() << "\n";
+  std::cout << "-- Building for: " << gen->GetName() << '\n';
 #endif
   this->SetGlobalGenerator(std::move(gen));
 }
@@ -2596,7 +2708,7 @@
   }
   this->GlobalGenerator->Generate();
   if (!this->GraphVizFile.empty()) {
-    std::cout << "Generate graphviz: " << this->GraphVizFile << std::endl;
+    std::cout << "Generate graphviz: " << this->GraphVizFile << '\n';
     this->GenerateGraphViz(this->GraphVizFile);
   }
   if (this->WarnUnusedCli) {
@@ -2837,17 +2949,15 @@
   std::vector<cmDocumentationEntry>& v)
 {
   const auto defaultGenerator = this->EvaluateDefaultGlobalGenerator();
-  const std::string defaultName = defaultGenerator->GetName();
-  bool foundDefaultOne = false;
+  const auto defaultName = defaultGenerator->GetName();
+  auto foundDefaultOne = false;
 
   for (const auto& g : this->Generators) {
-    cmDocumentationEntry e;
-    g->GetDocumentation(e);
-    if (!foundDefaultOne && cmHasPrefix(e.Name, defaultName)) {
-      e.CustomNamePrefix = '*';
+    v.emplace_back(g->GetDocumentation());
+    if (!foundDefaultOne && cmHasPrefix(v.back().Name, defaultName)) {
+      v.back().CustomNamePrefix = '*';
       foundDefaultOne = true;
     }
-    v.push_back(std::move(e));
   }
 }
 
@@ -2860,21 +2970,14 @@
 
     // Aliases:
     for (std::string const& a : eg->Aliases) {
-      cmDocumentationEntry e;
-      e.Name = a;
-      e.Brief = doc;
-      v.push_back(std::move(e));
+      v.emplace_back(cmDocumentationEntry{ a, doc });
     }
 
     // Full names:
-    const std::vector<std::string> generators =
-      eg->GetSupportedGlobalGenerators();
-    for (std::string const& g : generators) {
-      cmDocumentationEntry e;
-      e.Name =
-        cmExternalMakefileProjectGenerator::CreateFullGeneratorName(g, name);
-      e.Brief = doc;
-      v.push_back(std::move(e));
+    for (std::string const& g : eg->GetSupportedGlobalGenerators()) {
+      v.emplace_back(cmDocumentationEntry{
+        cmExternalMakefileProjectGenerator::CreateFullGeneratorName(g, name),
+        doc });
     }
   }
 }
@@ -2893,7 +2996,7 @@
   cmDocumentation doc;
   auto generators = this->GetGeneratorsDocumentation();
   doc.AppendSection("Generators", generators);
-  std::cerr << "\n";
+  std::cerr << '\n';
   doc.PrintDocumentation(cmDocumentation::ListGenerators, std::cerr);
 #endif
 }
@@ -2945,7 +3048,7 @@
     if (verbose) {
       std::ostringstream msg;
       msg << "Re-run cmake missing file: " << this->CheckBuildSystemArgument
-          << "\n";
+          << '\n';
       cmSystemTools::Stdout(msg.str());
     }
     return 1;
@@ -2965,7 +3068,7 @@
     if (verbose) {
       std::ostringstream msg;
       msg << "Re-run cmake error reading : " << this->CheckBuildSystemArgument
-          << "\n";
+          << '\n';
       cmSystemTools::Stdout(msg.str());
     }
     // There was an error reading the file.  Just rerun.
@@ -2996,9 +3099,8 @@
   for (std::string const& p : products) {
     if (!(cmSystemTools::FileExists(p) || cmSystemTools::FileIsSymlink(p))) {
       if (verbose) {
-        std::ostringstream msg;
-        msg << "Re-run cmake, missing byproduct: " << p << "\n";
-        cmSystemTools::Stdout(msg.str());
+        cmSystemTools::Stdout(
+          cmStrCat("Re-run cmake, missing byproduct: ", p, '\n'));
       }
       return 1;
     }
@@ -3063,7 +3165,7 @@
       if (verbose) {
         std::ostringstream msg;
         msg << "Re-run cmake file: " << out_oldest
-            << " older than: " << dep_newest << "\n";
+            << " older than: " << dep_newest << '\n';
         cmSystemTools::Stdout(msg.str());
       }
       return 1;
@@ -3239,7 +3341,7 @@
       // permissions are questionable or some other process has deleted the
       // directory
       std::cerr << "Failed to change to directory " << destPath << " : "
-                << std::strerror(workdir.GetLastResult()) << std::endl;
+                << std::strerror(workdir.GetLastResult()) << '\n';
       return 1;
     }
     std::vector<std::string> args2;
@@ -3276,96 +3378,6 @@
   return 0;
 }
 
-static bool cmakeCheckStampFile(const std::string& stampName)
-{
-  // The stamp file does not exist.  Use the stamp dependencies to
-  // determine whether it is really out of date.  This works in
-  // conjunction with cmLocalVisualStudio7Generator to avoid
-  // repeatedly re-running CMake when the user rebuilds the entire
-  // solution.
-  std::string stampDepends = cmStrCat(stampName, ".depend");
-#if defined(_WIN32) || defined(__CYGWIN__)
-  cmsys::ifstream fin(stampDepends.c_str(), std::ios::in | std::ios::binary);
-#else
-  cmsys::ifstream fin(stampDepends.c_str());
-#endif
-  if (!fin) {
-    // The stamp dependencies file cannot be read.  Just assume the
-    // build system is really out of date.
-    std::cout << "CMake is re-running because " << stampName
-              << " dependency file is missing.\n";
-    return false;
-  }
-
-  // Compare the stamp dependencies against the dependency file itself.
-  {
-    cmFileTimeCache ftc;
-    std::string dep;
-    while (cmSystemTools::GetLineFromStream(fin, dep)) {
-      int result;
-      if (!dep.empty() && dep[0] != '#' &&
-          (!ftc.Compare(stampDepends, dep, &result) || result < 0)) {
-        // The stamp depends file is older than this dependency.  The
-        // build system is really out of date.
-        std::cout << "CMake is re-running because " << stampName
-                  << " is out-of-date.\n";
-        std::cout << "  the file '" << dep << "'\n";
-        std::cout << "  is newer than '" << stampDepends << "'\n";
-        std::cout << "  result='" << result << "'\n";
-        return false;
-      }
-    }
-  }
-
-  // The build system is up to date.  The stamp file has been removed
-  // by the VS IDE due to a "rebuild" request.  Restore it atomically.
-  std::ostringstream stampTempStream;
-  stampTempStream << stampName << ".tmp" << cmSystemTools::RandomSeed();
-  std::string stampTemp = stampTempStream.str();
-  {
-    // TODO: Teach cmGeneratedFileStream to use a random temp file (with
-    // multiple tries in unlikely case of conflict) and use that here.
-    cmsys::ofstream stamp(stampTemp.c_str());
-    stamp << "# CMake generation timestamp file for this directory.\n";
-  }
-  std::string err;
-  if (cmSystemTools::RenameFile(stampTemp, stampName,
-                                cmSystemTools::Replace::Yes, &err) ==
-      cmSystemTools::RenameResult::Success) {
-    // CMake does not need to re-run because the stamp file is up-to-date.
-    return true;
-  }
-  cmSystemTools::RemoveFile(stampTemp);
-  cmSystemTools::Error(
-    cmStrCat("Cannot restore timestamp \"", stampName, "\": ", err));
-  return false;
-}
-
-static bool cmakeCheckStampList(const std::string& stampList)
-{
-  // If the stamp list does not exist CMake must rerun to generate it.
-  if (!cmSystemTools::FileExists(stampList)) {
-    std::cout << "CMake is re-running because generate.stamp.list "
-              << "is missing.\n";
-    return false;
-  }
-  cmsys::ifstream fin(stampList.c_str());
-  if (!fin) {
-    std::cout << "CMake is re-running because generate.stamp.list "
-              << "could not be read.\n";
-    return false;
-  }
-
-  // Check each stamp.
-  std::string stampName;
-  while (cmSystemTools::GetLineFromStream(fin, stampName)) {
-    if (!cmakeCheckStampFile(stampName)) {
-      return false;
-    }
-  }
-  return true;
-}
-
 void cmake::IssueMessage(MessageType t, std::string const& text,
                          cmListFileBacktrace const& backtrace) const
 {
diff --git a/Source/cmake.h b/Source/cmake.h
index 6b585a1..325c0c5 100644
--- a/Source/cmake.h
+++ b/Source/cmake.h
@@ -18,6 +18,7 @@
 #include <cm/string_view>
 #include <cmext/string_view>
 
+#include "cmDocumentationEntry.h"
 #include "cmGeneratedFileStream.h"
 #include "cmInstalledFile.h"
 #include "cmListFileCache.h"
@@ -45,7 +46,6 @@
 class cmMessenger;
 class cmVariableWatch;
 struct cmBuildOptions;
-struct cmDocumentationEntry;
 
 /** \brief Represents a cmake invocation.
  *
@@ -793,37 +793,10 @@
 #if !defined(CMAKE_BOOTSTRAP)
   std::unique_ptr<cmMakefileProfilingData> ProfilingOutput;
 #endif
-};
 
-#define CMAKE_STANDARD_OPTIONS_TABLE                                          \
-  { "-S <path-to-source>", "Explicitly specify a source directory." },        \
-    { "-B <path-to-build>", "Explicitly specify a build directory." },        \
-    { "-C <initial-cache>", "Pre-load a script to populate the cache." },     \
-    { "-D <var>[:<type>]=<value>", "Create or update a cmake cache entry." }, \
-    { "-U <globbing_expr>", "Remove matching entries from CMake cache." },    \
-    { "-G <generator-name>", "Specify a build system generator." },           \
-    { "-T <toolset-name>",                                                    \
-      "Specify toolset name if supported by generator." },                    \
-    { "-A <platform-name>",                                                   \
-      "Specify platform name if supported by generator." },                   \
-    { "--toolchain <file>",                                                   \
-      "Specify toolchain file [CMAKE_TOOLCHAIN_FILE]." },                     \
-    { "--install-prefix <directory>",                                         \
-      "Specify install directory [CMAKE_INSTALL_PREFIX]." },                  \
-    { "-Wdev", "Enable developer warnings." },                                \
-    { "-Wno-dev", "Suppress developer warnings." },                           \
-    { "-Werror=dev", "Make developer warnings errors." },                     \
-    { "-Wno-error=dev", "Make developer warnings not errors." },              \
-    { "-Wdeprecated", "Enable deprecation warnings." },                       \
-    { "-Wno-deprecated", "Suppress deprecation warnings." },                  \
-    { "-Werror=deprecated",                                                   \
-      "Make deprecated macro and function warnings "                          \
-      "errors." },                                                            \
-  {                                                                           \
-    "-Wno-error=deprecated",                                                  \
-      "Make deprecated macro and function warnings "                          \
-      "not errors."                                                           \
-  }
+public:
+  static cmDocumentationEntry CMAKE_STANDARD_OPTIONS_TABLE[18];
+};
 
 #define FOR_EACH_C90_FEATURE(F) F(c_function_prototypes)
 
diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx
index 9f23667..a155787 100644
--- a/Source/cmakemain.cxx
+++ b/Source/cmakemain.cxx
@@ -24,7 +24,7 @@
 #include "cmBuildOptions.h"
 #include "cmCommandLineArgument.h"
 #include "cmConsoleBuf.h"
-#include "cmDocumentationEntry.h" // IWYU pragma: keep
+#include "cmDocumentationEntry.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageMetadata.h"
@@ -46,30 +46,28 @@
 
 namespace {
 #ifndef CMAKE_BOOTSTRAP
-const char* cmDocumentationName[][2] = {
-  { nullptr, "  cmake - Cross-Platform Makefile Generator." },
-  { nullptr, nullptr }
+const cmDocumentationEntry cmDocumentationName = {
+  {},
+  "  cmake - Cross-Platform Makefile Generator."
 };
 
-const char* cmDocumentationUsage[][2] = {
-  { nullptr,
+const cmDocumentationEntry cmDocumentationUsage[2] = {
+  { {},
     "  cmake [options] <path-to-source>\n"
     "  cmake [options] <path-to-existing-build>\n"
     "  cmake [options] -S <path-to-source> -B <path-to-build>" },
-  { nullptr,
+  { {},
     "Specify a source directory to (re-)generate a build system for "
     "it in the current working directory.  Specify an existing build "
-    "directory to re-generate its build system." },
-  { nullptr, nullptr }
+    "directory to re-generate its build system." }
 };
 
-const char* cmDocumentationUsageNote[][2] = {
-  { nullptr, "Run 'cmake --help' for more information." },
-  { nullptr, nullptr }
+const cmDocumentationEntry cmDocumentationUsageNote = {
+  {},
+  "Run 'cmake --help' for more information."
 };
 
-const char* cmDocumentationOptions[][2] = {
-  CMAKE_STANDARD_OPTIONS_TABLE,
+const cmDocumentationEntry cmDocumentationOptions[31] = {
   { "--preset <preset>,--preset=<preset>", "Specify a configure preset." },
   { "--list-presets[=<type>]", "List available presets." },
   { "-E", "CMake command mode." },
@@ -113,15 +111,12 @@
   { "--compile-no-warning-as-error",
     "Ignore COMPILE_WARNING_AS_ERROR property and "
     "CMAKE_COMPILE_WARNING_AS_ERROR variable." },
-#  if !defined(CMAKE_BOOTSTRAP)
   { "--profiling-format=<fmt>",
     "Output data for profiling CMake scripts. Supported formats: "
     "google-trace" },
   { "--profiling-output=<file>",
     "Select an output path for the profiling data enabled through "
-    "--profiling-format." },
-#  endif
-  { nullptr, nullptr }
+    "--profiling-format." }
 };
 
 #endif
@@ -228,8 +223,9 @@
     }
     doc.AppendSection("Generators", generators);
     doc.PrependSection("Options", cmDocumentationOptions);
+    doc.PrependSection("Options", cmake::CMAKE_STANDARD_OPTIONS_TABLE);
 
-    return doc.PrintRequestedDocumentation(std::cout) ? 0 : 1;
+    return !doc.PrintRequestedDocumentation(std::cout);
   }
 #else
   if (ac == 1) {
diff --git a/Source/ctest.cxx b/Source/ctest.cxx
index 363f473..fa38a65 100644
--- a/Source/ctest.cxx
+++ b/Source/ctest.cxx
@@ -11,21 +11,21 @@
 #include "cmCTest.h"
 #include "cmConsoleBuf.h"
 #include "cmDocumentation.h"
+#include "cmDocumentationEntry.h"
 #include "cmSystemTools.h"
 
 #include "CTest/cmCTestLaunch.h"
 #include "CTest/cmCTestScriptHandler.h"
 
-static const char* cmDocumentationName[][2] = {
-  { nullptr, "  ctest - Testing driver provided by CMake." },
-  { nullptr, nullptr }
+namespace {
+const cmDocumentationEntry cmDocumentationName = {
+  {},
+  "  ctest - Testing driver provided by CMake."
 };
 
-static const char* cmDocumentationUsage[][2] = { { nullptr,
-                                                   "  ctest [options]" },
-                                                 { nullptr, nullptr } };
+const cmDocumentationEntry cmDocumentationUsage = { {}, "  ctest [options]" };
 
-static const char* cmDocumentationOptions[][2] = {
+const cmDocumentationEntry cmDocumentationOptions[74] = {
   { "--preset <preset>, --preset=<preset>",
     "Read arguments from a test preset." },
   { "--list-presets", "List available test presets." },
@@ -155,9 +155,9 @@
   { "--no-compress-output", "Do not compress test output when submitting." },
   { "--print-labels", "Print all available test labels." },
   { "--no-tests=<[error|ignore]>",
-    "Regard no tests found either as 'error' or 'ignore' it." },
-  { nullptr, nullptr }
+    "Regard no tests found either as 'error' or 'ignore' it." }
 };
+} // anonymous namespace
 
 // this is a test driver program for cmCTest.
 int main(int argc, char const* const* argv)
@@ -186,8 +186,7 @@
 
   if (cmSystemTools::GetCurrentWorkingDirectory().empty()) {
     cmCTestLog(&inst, ERROR_MESSAGE,
-               "Current working directory cannot be established."
-                 << std::endl);
+               "Current working directory cannot be established.\n");
     return 1;
   }
 
@@ -199,10 +198,9 @@
         cmSystemTools::FileExists("DartTestfile.txt"))) {
     if (argc == 1) {
       cmCTestLog(&inst, ERROR_MESSAGE,
-                 "*********************************"
-                   << std::endl
-                   << "No test configuration file found!" << std::endl
-                   << "*********************************" << std::endl);
+                 "*********************************\n"
+                 "No test configuration file found!\n"
+                 "*********************************\n");
     }
     cmDocumentation doc;
     doc.addCTestStandardDocSections();
@@ -216,7 +214,7 @@
       doc.SetSection("Name", cmDocumentationName);
       doc.SetSection("Usage", cmDocumentationUsage);
       doc.PrependSection("Options", cmDocumentationOptions);
-      return doc.PrintRequestedDocumentation(std::cout) ? 0 : 1;
+      return !doc.PrintRequestedDocumentation(std::cout);
     }
   }
 
diff --git a/Tests/RunCMake/ExportImport/BuildInstallInterfaceGenex-export.cmake b/Tests/RunCMake/ExportImport/BuildInstallInterfaceGenex-export.cmake
new file mode 100644
index 0000000..94076bb
--- /dev/null
+++ b/Tests/RunCMake/ExportImport/BuildInstallInterfaceGenex-export.cmake
@@ -0,0 +1,14 @@
+enable_language(C)
+
+add_library(mainlib STATIC foo.c)
+target_compile_definitions(mainlib INTERFACE
+  $<BUILD_LOCAL_INTERFACE:BUILD_LOCAL_INTERFACE>
+  $<BUILD_INTERFACE:BUILD_INTERFACE>
+  $<INSTALL_INTERFACE:INSTALL_INTERFACE>
+  )
+add_library(locallib STATIC locallib.c)
+target_link_libraries(locallib PRIVATE mainlib)
+
+install(TARGETS mainlib EXPORT export)
+install(EXPORT export DESTINATION lib/cmake/install FILE install-config.cmake NAMESPACE install::)
+export(EXPORT export FILE build-config.cmake NAMESPACE build::)
diff --git a/Tests/RunCMake/ExportImport/BuildInstallInterfaceGenex-import.cmake b/Tests/RunCMake/ExportImport/BuildInstallInterfaceGenex-import.cmake
new file mode 100644
index 0000000..3fe5fae
--- /dev/null
+++ b/Tests/RunCMake/ExportImport/BuildInstallInterfaceGenex-import.cmake
@@ -0,0 +1,9 @@
+enable_language(C)
+
+find_package(build REQUIRED)
+find_package(install REQUIRED)
+
+add_library(buildlib STATIC buildlib.c)
+target_link_libraries(buildlib PRIVATE build::mainlib)
+add_library(installlib STATIC installlib.c)
+target_link_libraries(installlib PRIVATE install::mainlib)
diff --git a/Tests/RunCMake/ExportImport/RunCMakeTest.cmake b/Tests/RunCMake/ExportImport/RunCMakeTest.cmake
index d07fca2..b730047 100644
--- a/Tests/RunCMake/ExportImport/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ExportImport/RunCMakeTest.cmake
@@ -22,3 +22,28 @@
 endfunction()
 
 run_ExportImport_test(SharedDep)
+
+function(run_ExportImportBuildInstall_test case)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-export-build)
+  set(CMAKE_INSTALL_PREFIX ${RunCMake_TEST_BINARY_DIR}/root)
+  if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
+  endif()
+  run_cmake(${case}-export)
+  unset(RunCMake_TEST_OPTIONS)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(${case}-export-build ${CMAKE_COMMAND} --build . --config Debug)
+  run_cmake_command(${case}-export-install ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DBUILD_TYPE=Debug -P cmake_install.cmake)
+  unset(RunCMake_TEST_NO_CLEAN)
+
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-import-build)
+  run_cmake_with_options(${case}-import
+    -Dbuild_DIR=${RunCMake_BINARY_DIR}/${case}-export-build
+    -Dinstall_DIR=${CMAKE_INSTALL_PREFIX}/lib/cmake/install
+    )
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(${case}-import-build ${CMAKE_COMMAND} --build . --config Debug)
+  unset(RunCMake_TEST_NO_CLEAN)
+endfunction()
+
+run_ExportImportBuildInstall_test(BuildInstallInterfaceGenex)
diff --git a/Tests/RunCMake/ExportImport/buildlib.c b/Tests/RunCMake/ExportImport/buildlib.c
new file mode 100644
index 0000000..ac19310
--- /dev/null
+++ b/Tests/RunCMake/ExportImport/buildlib.c
@@ -0,0 +1,8 @@
+#if !(!defined(BUILD_LOCAL_INTERFACE) && defined(BUILD_INTERFACE) &&          \
+      !defined(INSTALL_INTERFACE))
+#  error "Incorrect compile definitions"
+#endif
+
+void buildlib(void)
+{
+}
diff --git a/Tests/RunCMake/ExportImport/installlib.c b/Tests/RunCMake/ExportImport/installlib.c
new file mode 100644
index 0000000..00d503c
--- /dev/null
+++ b/Tests/RunCMake/ExportImport/installlib.c
@@ -0,0 +1,8 @@
+#if !(!defined(BUILD_LOCAL_INTERFACE) && !defined(BUILD_INTERFACE) &&         \
+      defined(INSTALL_INTERFACE))
+#  error "Incorrect compile definitions"
+#endif
+
+void installlib(void)
+{
+}
diff --git a/Tests/RunCMake/ExportImport/locallib.c b/Tests/RunCMake/ExportImport/locallib.c
new file mode 100644
index 0000000..f9e3d8d
--- /dev/null
+++ b/Tests/RunCMake/ExportImport/locallib.c
@@ -0,0 +1,8 @@
+#if !(defined(BUILD_LOCAL_INTERFACE) && defined(BUILD_INTERFACE) &&           \
+      !defined(INSTALL_INTERFACE))
+#  error "Incorrect compile definitions"
+#endif
+
+void locallib(void)
+{
+}
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/RunCMakeTest.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/RunCMakeTest.cmake
index ac0843e..b613ad1 100644
--- a/Tests/RunCMake/GenEx-TARGET_PROPERTY/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/RunCMakeTest.cmake
@@ -13,3 +13,10 @@
 run_cmake(LinkImplementationCycle6)
 run_cmake(LOCATION)
 run_cmake(SOURCES)
+
+block()
+  run_cmake(Scope)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Scope-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(Scope-build ${CMAKE_COMMAND} --build . --config Debug)
+endblock()
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/Scope-build-stdout.txt b/Tests/RunCMake/GenEx-TARGET_PROPERTY/Scope-build-stdout.txt
new file mode 100644
index 0000000..fefad22
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/Scope-build-stdout.txt
@@ -0,0 +1,6 @@
+.*iface scope1: 'SCOPED_A_1;SCOPED_B_1'
+.*iface scope2: 'SCOPED_A_2'
+.*iface scope2 in scope1: 'SCOPED_A_2'
+.*custom scope1: 'SCOPED_A_1;SCOPED_B_1'
+.*custom scope2: 'SCOPED_A_2'
+.*custom scope2 in scope1: 'SCOPED_A_1'
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/Scope.c b/Tests/RunCMake/GenEx-TARGET_PROPERTY/Scope.c
new file mode 100644
index 0000000..a4bec6f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/Scope.c
@@ -0,0 +1,12 @@
+#ifndef SCOPED_A_1
+#  error "SCOPED_A_1 not defined"
+#endif
+#ifndef SCOPED_B_1
+#  error "SCOPED_B_1 not defined"
+#endif
+#ifndef SCOPED_A_2
+#  error "SCOPED_A_2 not defined"
+#endif
+void Scope(void)
+{
+}
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/Scope.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/Scope.cmake
new file mode 100644
index 0000000..48a878a
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/Scope.cmake
@@ -0,0 +1,19 @@
+enable_language(C)
+
+add_subdirectory(Scope1)
+add_subdirectory(Scope2)
+
+add_library(Scope Scope.c)
+target_link_libraries(Scope PRIVATE
+  scope1_iface
+  scope2_iface
+  )
+
+add_custom_target(Custom ALL VERBATIM
+  COMMAND ${CMAKE_COMMAND} -E echo "iface scope1: '$<TARGET_PROPERTY:scope1_iface,INTERFACE_COMPILE_DEFINITIONS>'"
+  COMMAND ${CMAKE_COMMAND} -E echo "iface scope2: '$<TARGET_PROPERTY:scope2_iface,INTERFACE_COMPILE_DEFINITIONS>'"
+  COMMAND ${CMAKE_COMMAND} -E echo "iface scope2 in scope1: '$<TARGET_GENEX_EVAL:scope1_iface,$<TARGET_PROPERTY:scope2_iface,INTERFACE_COMPILE_DEFINITIONS>>'"
+  COMMAND ${CMAKE_COMMAND} -E echo "custom scope1: '$<TARGET_GENEX_EVAL:scope1_iface,$<TARGET_PROPERTY:scope1_iface,CUSTOM_PROP>>'"
+  COMMAND ${CMAKE_COMMAND} -E echo "custom scope2: '$<TARGET_GENEX_EVAL:scope2_iface,$<TARGET_PROPERTY:scope2_iface,CUSTOM_PROP>>'"
+  COMMAND ${CMAKE_COMMAND} -E echo "custom scope2 in scope1: '$<TARGET_GENEX_EVAL:scope1_iface,$<TARGET_PROPERTY:scope2_iface,CUSTOM_PROP>>'"
+  )
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/Scope1/CMakeLists.txt b/Tests/RunCMake/GenEx-TARGET_PROPERTY/Scope1/CMakeLists.txt
new file mode 100644
index 0000000..d546267
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/Scope1/CMakeLists.txt
@@ -0,0 +1,15 @@
+add_library(scopedA INTERFACE IMPORTED)
+set_property(TARGET scopedA PROPERTY INTERFACE_COMPILE_DEFINITIONS "SCOPED_A_1")
+
+add_library(scopedB INTERFACE IMPORTED)
+set_property(TARGET scopedB PROPERTY INTERFACE_COMPILE_DEFINITIONS "SCOPED_B_1")
+
+add_library(scope1_iface INTERFACE)
+set_property(TARGET scope1_iface PROPERTY INTERFACE_COMPILE_DEFINITIONS
+  "$<TARGET_PROPERTY:scopedA,INTERFACE_COMPILE_DEFINITIONS>"
+  "$<TARGET_PROPERTY:scopedB,INTERFACE_COMPILE_DEFINITIONS>"
+  )
+set_property(TARGET scope1_iface PROPERTY CUSTOM_PROP
+  "$<TARGET_PROPERTY:scopedA,INTERFACE_COMPILE_DEFINITIONS>"
+  "$<TARGET_PROPERTY:scopedB,INTERFACE_COMPILE_DEFINITIONS>"
+  )
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/Scope2/CMakeLists.txt b/Tests/RunCMake/GenEx-TARGET_PROPERTY/Scope2/CMakeLists.txt
new file mode 100644
index 0000000..a6d7e6b
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/Scope2/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_library(scopedA INTERFACE IMPORTED)
+set_property(TARGET scopedA PROPERTY INTERFACE_COMPILE_DEFINITIONS "SCOPED_A_2")
+
+add_library(scope2_iface INTERFACE)
+set_property(TARGET scope2_iface PROPERTY INTERFACE_COMPILE_DEFINITIONS
+  "$<TARGET_PROPERTY:scopedA,INTERFACE_COMPILE_DEFINITIONS>"
+  )
+set_property(TARGET scope2_iface PROPERTY CUSTOM_PROP
+  "$<TARGET_PROPERTY:scopedA,INTERFACE_COMPILE_DEFINITIONS>"
+  )
diff --git a/Tests/RunCMake/Swift/NoWorkToDo-nowork-stdout.txt b/Tests/RunCMake/Swift/NoWorkToDo-nowork-stdout.txt
new file mode 100644
index 0000000..60a9228
--- /dev/null
+++ b/Tests/RunCMake/Swift/NoWorkToDo-nowork-stdout.txt
@@ -0,0 +1 @@
+^ninja: no work to do
diff --git a/Tests/RunCMake/Swift/NoWorkToDo.cmake b/Tests/RunCMake/Swift/NoWorkToDo.cmake
new file mode 100644
index 0000000..e86a861
--- /dev/null
+++ b/Tests/RunCMake/Swift/NoWorkToDo.cmake
@@ -0,0 +1,2 @@
+enable_language(Swift)
+add_executable(hello hello.swift)
diff --git a/Tests/RunCMake/Swift/RunCMakeTest.cmake b/Tests/RunCMake/Swift/RunCMakeTest.cmake
index 21d5a25..7f4464d 100644
--- a/Tests/RunCMake/Swift/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Swift/RunCMakeTest.cmake
@@ -14,6 +14,16 @@
       run_cmake(SwiftMultiArch)
       unset(RunCMake_TEST_OPTIONS)
     endif()
+
+    # Test that a second build with no changes does nothing.
+    block()
+      run_cmake(NoWorkToDo)
+      set(RunCMake_TEST_NO_CLEAN 1)
+      set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/NoWorkToDo-build)
+      set(RunCMake_TEST_OUTPUT_MERGE 1)
+      run_cmake_command(NoWorkToDo-build ${CMAKE_COMMAND} --build .)
+      run_cmake_command(NoWorkToDo-nowork ${CMAKE_COMMAND} --build . -- -d explain)
+    endblock()
   endif()
 elseif(RunCMake_GENERATOR STREQUAL "Ninja Multi-Config")
   if(CMAKE_Swift_COMPILER)
diff --git a/Tests/RunCMake/Swift/hello.swift b/Tests/RunCMake/Swift/hello.swift
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/Swift/hello.swift