diff --git a/Documentation/Dependencies.dox b/Documentation/Dependencies.dox
new file mode 100644
index 0000000000000000000000000000000000000000..c5a9e4db19ba74fce4414c92128e2ac40b7ea3d6
--- /dev/null
+++ b/Documentation/Dependencies.dox
@@ -0,0 +1,6 @@
+/*! \defgroup Dependencies Library dependencies
+
+\image html cmake-dependencies.svg "cmake-dependencies" width=300%
+\image html cpp-dependencies.svg "cpp-dependencies" width=300%
+
+*/
diff --git a/Documentation/mainpage.dox.in b/Documentation/mainpage.dox.in
index 46b7acb812ec613c19db445637a17a087bba1cb1..ef21f10b414458e5e8dbe3fb8100b36da0825bd8 100644
--- a/Documentation/mainpage.dox.in
+++ b/Documentation/mainpage.dox.in
@@ -31,9 +31,9 @@
  * - Data Explorer (GUI) manual: https://gitlab.opengeosys.org/ogs/data_explorer_manual/-/jobs/artifacts/master/raw/ogsde-man.pdf?job=build
  * - Discussion forum: https://discourse.opengeosys.org
  *
- *
  * \section internal_modules Internal Modules
  *
+ * ${_subpage_dependencies}
  * * \subpage ODESolver
  * * \subpage ExternalODESolverInterface
  *
diff --git a/config-cpp-dependencies.txt b/config-cpp-dependencies.txt
index f9a91ef8bc74f9c7adebcf2a912e3846ef644c50..b1bf326da097fb36e7016d3c270ef05121a42585 100644
--- a/config-cpp-dependencies.txt
+++ b/config-cpp-dependencies.txt
@@ -9,6 +9,21 @@ versionUsed: 2
 # Company name to use in generated CMakeLists' copyright statement.
 companyName: OpenGeoSys Community (www.opengeosys.org)
 
+# License text to include directly after the copyright statement.
+licenseString: """
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
 # Tag used in generated CMakeLists. Don't change unless you also update your CMakeLists.
 regenTag: GENERATED BY CPP-DEPENDENCIES
 
@@ -40,6 +55,42 @@ componentLocUpperLimit: 20000
 # logical units, which are then easy to mix up and conflate.
 fileLocUpperLimit: 2000
 
+# Whether custom sections, like "set_target_property(...)", from an existing CMakeLists.txt
+# file should be reused.
+reuseCustomSections: false
+
+# Aliases for CMake command add_library. Each alias is assumed to take similar arguments as the
+# vanilla CMake command.
+# Each alias must be on its own line. The last line should only contain the closing bracket.
+addLibraryAlias: [
+  ogs_add_library
+]
+
+# Aliases for CMake command add_executable. Each alias is assumed to take similar arguments as the
+# vanilla CMake command.
+# Each alias must be on its own line. The last line should only contain the closing bracket.
+addExecutableAlias: [
+  ogs_add_executable
+]
+
+#addIgnores: [
+# Example/ThirdParty
+# Example/test.txt
+# ]
+
 # List of folder paths (from the root) that should be completely ignored. May contain multiple
 # space-separated values, including values with spaces escaped with quotation marks.
-blacklist: build Build "Visual Studio Projects" unistd.h console.h stdint.h windows.h library.h endian.h rle.h Applications/Utils SimpleTests ThirdParty
+blacklist: [
+  build
+  Build
+  Visual Studio Projects
+  unistd.h
+  console.h
+  stdint.h
+  windows.h
+  library.h
+  endian.h
+  rle.h
+  Applications/Utils
+  Tests
+]
diff --git a/scripts/ci/jobs/build-docs.yml b/scripts/ci/jobs/build-docs.yml
index cfc139f6360af548844fe226f02c13bb23f04bf2..a268fbbf7d4d3268ede168c7447e4425dd6fec9b 100644
--- a/scripts/ci/jobs/build-docs.yml
+++ b/scripts/ci/jobs/build-docs.yml
@@ -16,17 +16,30 @@ build docs:
     when: runner_system_failure
   variables:
     DOX_WARNINGS_THRESHOLD: 12
-  before_script:
+  script:
     # HACK to easier linking to the generated pages
     - echo '<meta http-equiv="REFRESH" content="0;URL=build/docs/index.html">' >> Doxygen.html
     - mkdir -p build
+    # cpp-dependencies
+    - NUM_CYCLES=`/opt/cppcheck/bin/cpp-dependencies --stats|grep cycles|cut -d " " -f 2`
+    - echo "num_cycles $NUM_CYCLES" > metrics.txt
+    - /opt/cppcheck/bin/cpp-dependencies --graph cpp-dependencies.dot && dot -Tsvg cpp-dependencies.dot -o build/cpp-dependencies.svg
+    # lizard, default code complexity number (15), function line length 100, max arguments 10
+    - python /opt/lizard/lizard.py -l cpp -w -t 8 --length 100 --arguments 10 > lizard.txt || true
+    - cat lizard.txt
+    - NUM_LIZARD_ISSUES=`cat lizard.txt | wc -l`
+    - echo "lizard_issues $NUM_LIZARD_ISSUES" >> metrics.txt
+    - python /opt/lizard/lizard.py -l cpp -t 8 --html -o Lizard.html
+    - python /opt/lizard/lizard.py -EWordCount -x "./ThirdParty/*" -x "./Tests/*" || true # word-cloud
+    # build
     - cd build
-  script:
-    - cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DOGS_USE_CONAN=OFF -DOGS_USE_POETRY=OFF -DOGS_BUILD_PROCESSES=SteadyStateDiffusion
+    - cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DOGS_USE_CONAN=OFF -DOGS_USE_POETRY=OFF
+    - cmake . --graphviz=cmake-dependencies.dot && dot -Tsvg cmake-dependencies.dot -o cmake-dependencies.svg
+    - cmake . -DOGS_BUILD_PROCESSES=SteadyStateDiffusion
     - cmake --build . --target doc > >(tee make-docs.output)
     - cat DoxygenWarnings.log|grep -v 'too many nodes'
     - NUM_DOX_WARNINGS=`cat DoxygenWarnings.log|grep warning:|grep -v 'too many nodes'|wc -l|xargs`
-    - echo "doxygen_warnings $NUM_DOX_WARNINGS" > metrics.txt
+    - echo "doxygen_warnings $NUM_DOX_WARNINGS" >> metrics.txt
     - |
       if [[ ( "$NUM_DOX_WARNINGS" > "$DOX_WARNINGS_THRESHOLD" ) ]]; then
         echo "Error: Number of Doxygen warnings exceeded threshold –> $NUM_DOX_WARNINGS > $DOX_WARNINGS_THRESHOLD"
@@ -45,22 +58,27 @@ build docs:
         sshpass -p $DOXYSEARCH_PW ssh webdev@doxysearch.opengeosys.org doxyindexer -o /var/www/doxysearch.opengeosys.org/$CI_COMMIT_BRANCH /var/www/doxysearch.opengeosys.org/$CI_COMMIT_BRANCH/searchdata.xml
       fi
   artifacts:
-    expose_as: 'Doxygen'
+    expose_as: "Doxygen"
     paths:
       - Doxygen.html
       - build/docs
+      - build/cpp-dependencies.svg
+      - build/cmake-dependencies.svg
+      - Lizard.html
+      - lizard.txt
+      - codecloud.html
     expire_in: 1 week
     reports:
       metrics: build/metrics.txt
 
 check docs links:
   stage: check
-  tags: [ docker ]
+  tags: [docker]
   allow_failure: true
   image: $WEB_IMAGE
   needs: [meta, ci_images, "build docs"]
   extends:
-      - .rules-master-manual
+    - .rules-master-manual
   script:
     - cd build
     - >
diff --git a/scripts/cmake/CMakeGraphVizOptions.in.cmake b/scripts/cmake/CMakeGraphVizOptions.in.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..698b933cb7762331e7bc473b333b89f6d37c5a5f
--- /dev/null
+++ b/scripts/cmake/CMakeGraphVizOptions.in.cmake
@@ -0,0 +1 @@
+set(GRAPHVIZ_IGNORE_TARGETS testrunner "vtk.*")
diff --git a/scripts/cmake/DocumentationSetup.cmake b/scripts/cmake/DocumentationSetup.cmake
index a9ce78bab59f666d7c4fc7d00851e20697872a5f..2b9efb05d097381894e5dca0fdea62728b806e81 100644
--- a/scripts/cmake/DocumentationSetup.cmake
+++ b/scripts/cmake/DocumentationSetup.cmake
@@ -38,7 +38,9 @@ set(DOXYGEN_CITE_BIB_FILES
 set(DOXYGEN_QUIET YES)
 set(DOXYGEN_WARN_LOGFILE ${PROJECT_BINARY_DIR}/DoxygenWarnings.log)
 set(DOXYGEN_EXCLUDE_PATTERNS moc_* ui_* CMake*)
-set(DOXYGEN_IMAGE_PATH ${PROJECT_SOURCE_DIR}/Documentation/images)
+set(DOXYGEN_IMAGE_PATH ${PROJECT_SOURCE_DIR}
+                       ${PROJECT_SOURCE_DIR}/Documentation/images
+)
 set(DOXYGEN_SOURCE_BROWSER YES)
 set(DOXYGEN_INLINE_SOURCES YES)
 set(DOXYGEN_REFERENCED_BY_RELATION YES)
@@ -89,6 +91,9 @@ if($ENV{CI_COMMIT_BRANCH} MATCHES "master|^v[0-9]\.[0-9]\.[0-9]")
     )
     message(STATUS "Doxygen search server: ${DOXYGEN_SEARCHENGINE_URL}")
 endif()
+if(EXISTS ${PROJECT_BINARY_DIR}/cpp-dependencies.svg)
+    set(_subpage_dependencies "* \\subpage Dependencies")
+endif()
 configure_file(
     ${PROJECT_SOURCE_DIR}/Documentation/mainpage.dox.in
     ${PROJECT_BINARY_DIR}/DocAux/dox/mainpage.dox
diff --git a/scripts/cmake/Find.cmake b/scripts/cmake/Find.cmake
index fdf557b24b0e521b68ca352825abcd3b835ab182..f6dba5a458236fb1f8d8939a299bba6f1d64545a 100644
--- a/scripts/cmake/Find.cmake
+++ b/scripts/cmake/Find.cmake
@@ -10,6 +10,17 @@ if(${GIT_VERSION_STRING} VERSION_LESS ${ogs.minimum_version.git})
 endif()
 
 find_package(Doxygen OPTIONAL_COMPONENTS dot)
+if(TARGET Doxygen::dot)
+    # Create dependency graph in build dir with:
+    # ~~~
+    # cmake . --graphviz=cmake-dependencies.dot && \
+    #   dot -Tpng cmake-dependencies.dot -o cmake-dependencies.png
+    # ~~~
+    configure_file(
+        ${PROJECT_SOURCE_DIR}/scripts/cmake/CMakeGraphVizOptions.in.cmake
+        ${PROJECT_BINARY_DIR}/CMakeGraphVizOptions.cmake
+    )
+endif()
 
 # Find gnu profiler gprof
 find_program(GPROF_PATH gprof DOC "GNU profiler gprof" QUIET)