presets: Add graphviz support

Closes: #22164
diff --git a/Help/manual/cmake-presets.7.rst b/Help/manual/cmake-presets.7.rst
index ee348c1..c0f847c 100644
--- a/Help/manual/cmake-presets.7.rst
+++ b/Help/manual/cmake-presets.7.rst
@@ -267,6 +267,17 @@
   :variable:`CMAKE_TOOLCHAIN_FILE` value. It is allowed in preset files
   specifying version ``3`` or above.
 
+  ``graphviz``
+  An optional string representing the path to the graphviz input file,
+  that will contain all the library and executable dependencies
+  in the project. See the documentation for :module:`CMakeGraphVizOptions`
+  for more details.
+
+  This field supports `macro expansion`_. If a relative path is specified,
+  it is calculated relative to the build directory, and if not found,
+  relative to the source directory. It is allowed in preset files
+  specifying version ``10`` or above.
+
 ``binaryDir``
   An optional string representing the path to the output binary directory.
   This field supports `macro expansion`_. If a relative path is specified,
diff --git a/Help/manual/presets/schema.json b/Help/manual/presets/schema.json
index a7aee63..63fbdb7 100644
--- a/Help/manual/presets/schema.json
+++ b/Help/manual/presets/schema.json
@@ -491,6 +491,10 @@
             "properties": {
               "$comment": { "$ref": "#/definitions/$comment" }
             }
+          },
+          "graphviz": {
+            "type": "string",
+            "description": "An optional string specifying the path to graphviz dot file. Available in version 10 and higher."
           }
         }
       }
@@ -743,6 +747,7 @@
           "architecture": { "$ref": "#/definitions/configurePresetsArchitectureV10" },
           "toolset": { "$ref": "#/definitions/configurePresetsToolsetV10" },
           "toolchainFile": {},
+          "graphviz": {},
           "binaryDir": {},
           "installDir": {},
           "cmakeExecutable": {},
diff --git a/Source/cmCMakePresetsErrors.cxx b/Source/cmCMakePresetsErrors.cxx
index 1b88a0c..5919512 100644
--- a/Source/cmCMakePresetsErrors.cxx
+++ b/Source/cmCMakePresetsErrors.cxx
@@ -177,6 +177,12 @@
                   "support");
 }
 
+void GRAPHVIZ_FILE_UNSUPPORTED(cmJSONState* state)
+{
+  state->AddError(
+    "File version must be 10 or higher for graphviz preset support");
+}
+
 void CYCLIC_INCLUDE(const std::string& file, cmJSONState* state)
 {
   state->AddError(cmStrCat("Cyclic include among preset files: ", file));
diff --git a/Source/cmCMakePresetsErrors.h b/Source/cmCMakePresetsErrors.h
index b755c25..dda9274 100644
--- a/Source/cmCMakePresetsErrors.h
+++ b/Source/cmCMakePresetsErrors.h
@@ -70,6 +70,8 @@
 
 void TOOLCHAIN_FILE_UNSUPPORTED(cmJSONState* state);
 
+void GRAPHVIZ_FILE_UNSUPPORTED(cmJSONState* state);
+
 void CYCLIC_INCLUDE(const std::string& file, cmJSONState* state);
 
 void TEST_OUTPUT_TRUNCATION_UNSUPPORTED(cmJSONState* state);
diff --git a/Source/cmCMakePresetsGraph.cxx b/Source/cmCMakePresetsGraph.cxx
index 008f2dd..189f101 100644
--- a/Source/cmCMakePresetsGraph.cxx
+++ b/Source/cmCMakePresetsGraph.cxx
@@ -293,6 +293,12 @@
     out->ToolchainFile = toolchain;
   }
 
+  if (!preset.GraphVizFile.empty()) {
+    std::string graphVizFile = preset.GraphVizFile;
+    CHECK_EXPAND(out, graphVizFile, macroExpanders, graph.GetVersion(preset));
+    out->GraphVizFile = graphVizFile;
+  }
+
   for (auto& variable : out->CacheVariables) {
     if (variable.second) {
       CHECK_EXPAND(out, variable.second->Value, macroExpanders,
@@ -775,6 +781,7 @@
   InheritString(preset.BinaryDir, parent.BinaryDir);
   InheritString(preset.InstallDir, parent.InstallDir);
   InheritString(preset.ToolchainFile, parent.ToolchainFile);
+  InheritString(preset.GraphVizFile, parent.GraphVizFile);
   InheritOptionalValue(preset.WarnDev, parent.WarnDev);
   InheritOptionalValue(preset.ErrorDev, parent.ErrorDev);
   InheritOptionalValue(preset.WarnDeprecated, parent.WarnDeprecated);
diff --git a/Source/cmCMakePresetsGraph.h b/Source/cmCMakePresetsGraph.h
index a000e7d..6ff6a0e 100644
--- a/Source/cmCMakePresetsGraph.h
+++ b/Source/cmCMakePresetsGraph.h
@@ -120,6 +120,7 @@
     std::string Toolset;
     cm::optional<ArchToolsetStrategy> ToolsetStrategy;
     std::string ToolchainFile;
+    std::string GraphVizFile;
     std::string BinaryDir;
     std::string InstallDir;
 
diff --git a/Source/cmCMakePresetsGraphReadJSON.cxx b/Source/cmCMakePresetsGraphReadJSON.cxx
index 108dfa2..df6482d 100644
--- a/Source/cmCMakePresetsGraphReadJSON.cxx
+++ b/Source/cmCMakePresetsGraphReadJSON.cxx
@@ -611,6 +611,12 @@
       return false;
     }
 
+    // Support for graphviz argument added in version 10.
+    if (v < 10 && !preset.GraphVizFile.empty()) {
+      cmCMakePresetsErrors::GRAPHVIZ_FILE_UNSUPPORTED(&this->parseState);
+      return false;
+    }
+
     this->ConfigurePresetOrder.push_back(preset.Name);
   }
 
diff --git a/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx b/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx
index c52a187..8f49d69 100644
--- a/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx
+++ b/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx
@@ -272,6 +272,8 @@
     .Bind("toolset"_s, ToolsetHelper, false)
     .Bind("toolchainFile"_s, &ConfigurePreset::ToolchainFile,
           cmCMakePresetsGraphInternal::PresetStringHelper, false)
+    .Bind("graphviz"_s, &ConfigurePreset::GraphVizFile,
+          cmCMakePresetsGraphInternal::PresetStringHelper, false)
     .Bind("binaryDir"_s, &ConfigurePreset::BinaryDir,
           cmCMakePresetsGraphInternal::PresetStringHelper, false)
     .Bind("installDir"_s, &ConfigurePreset::InstallDir,
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 7c64cee..3a2c690 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -1075,10 +1075,7 @@
     CommandArgument{ "--graphviz", "No file specified for --graphviz",
                      CommandArgument::Values::One,
                      [](std::string const& value, cmake* state) -> bool {
-                       std::string path =
-                         cmSystemTools::CollapseFullPath(value);
-                       cmSystemTools::ConvertToUnixSlashes(path);
-                       state->GraphVizFile = path;
+                       state->SetGraphVizFile(value);
                        return true;
                      } },
 
@@ -1590,6 +1587,12 @@
       }
     }
 
+    if (!expandedPreset->GraphVizFile.empty()) {
+      if (this->GraphVizFile.empty()) {
+        this->SetGraphVizFile(expandedPreset->GraphVizFile);
+      }
+    }
+
     this->SetWarningFromPreset("dev", expandedPreset->WarnDev,
                                expandedPreset->ErrorDev);
     this->SetWarningFromPreset("deprecated", expandedPreset->WarnDeprecated,
diff --git a/Source/cmake.h b/Source/cmake.h
index 2d17ed2..cfe4edd 100644
--- a/Source/cmake.h
+++ b/Source/cmake.h
@@ -26,6 +26,7 @@
 #include "cmState.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmSystemTools.h"
 #include "cmValue.h"
 
 #if !defined(CMAKE_BOOTSTRAP)
@@ -298,6 +299,14 @@
     this->GeneratorToolsetSet = true;
   }
 
+  //! Set the name of the graphviz file.
+  void SetGraphVizFile(std::string const& ts)
+  {
+    std::string path = cmSystemTools::CollapseFullPath(ts);
+    cmSystemTools::ConvertToUnixSlashes(path);
+    this->GraphVizFile = path;
+  }
+
   bool IsAKnownSourceExtension(cm::string_view ext) const
   {
     return this->CLikeSourceFileExtensions.Test(ext) ||
diff --git a/Tests/RunCMake/CMakePresets/GraphvizValid-result.txt b/Tests/RunCMake/CMakePresets/GraphvizValid-result.txt
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/GraphvizValid-result.txt
@@ -0,0 +1 @@
+0
diff --git a/Tests/RunCMake/CMakePresets/GraphvizValid-stdout.txt b/Tests/RunCMake/CMakePresets/GraphvizValid-stdout.txt
new file mode 100644
index 0000000..43d0958
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/GraphvizValid-stdout.txt
@@ -0,0 +1,2 @@
+
+Generate graphviz: .+[\\/]my_graphviz\.dot
diff --git a/Tests/RunCMake/CMakePresets/GraphvizValid.cmake b/Tests/RunCMake/CMakePresets/GraphvizValid.cmake
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/GraphvizValid.cmake
diff --git a/Tests/RunCMake/CMakePresets/GraphvizValid.json.in b/Tests/RunCMake/CMakePresets/GraphvizValid.json.in
new file mode 100644
index 0000000..0a4ab6a
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/GraphvizValid.json.in
@@ -0,0 +1,11 @@
+{
+  "version": 10,
+  "configurePresets": [
+    {
+      "name": "GraphvizValid",
+      "generator": "@RunCMake_GENERATOR@",
+      "graphviz": "${sourceDir}/my_graphviz.dot",
+      "binaryDir": "${sourceDir}/build"
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema-result.txt b/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema-stderr.txt b/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema-stderr.txt
new file mode 100644
index 0000000..0b7b4d6
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema-stderr.txt
@@ -0,0 +1,2 @@
+^CMake Error: Could not read presets from .*
+File version must be 10 or higher for graphviz preset support
diff --git a/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema.cmake b/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema.cmake
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema.cmake
diff --git a/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema.json.in b/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema.json.in
new file mode 100644
index 0000000..85d0b1b
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema.json.in
@@ -0,0 +1,11 @@
+{
+  "version": 9,
+  "configurePresets": [
+    {
+      "name": "GraphvizValid",
+      "generator": "@RunCMake_GENERATOR@",
+      "graphviz": "${sourceDir}/my_graphviz.dot",
+      "binaryDir": "${sourceDir}/build"
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresets/NoGraphvizValid-result.txt b/Tests/RunCMake/CMakePresets/NoGraphvizValid-result.txt
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/NoGraphvizValid-result.txt
@@ -0,0 +1 @@
+0
diff --git a/Tests/RunCMake/CMakePresets/NoGraphvizValid.cmake b/Tests/RunCMake/CMakePresets/NoGraphvizValid.cmake
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/NoGraphvizValid.cmake
diff --git a/Tests/RunCMake/CMakePresets/NoGraphvizValid.json.in b/Tests/RunCMake/CMakePresets/NoGraphvizValid.json.in
new file mode 100644
index 0000000..b689833
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/NoGraphvizValid.json.in
@@ -0,0 +1,10 @@
+{
+  "version": 10,
+  "configurePresets": [
+    {
+      "name": "NoGraphvizValid",
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/build"
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
index 081f3bb..a92a4c4 100644
--- a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
@@ -98,6 +98,11 @@
 run_cmake_presets(CommentValidFull)
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 1)
 run_cmake_presets(CommentValidOldSchema)
+set(CMakePresets_SCHEMA_EXPECTED_RESULT 0)
+run_cmake_presets(NoGraphvizValid)
+run_cmake_presets(GraphvizValid)
+set(CMakePresets_SCHEMA_EXPECTED_RESULT 1)
+run_cmake_presets(GraphvizValidOldSchema)
 run_cmake_presets(JSONParseError)
 run_cmake_presets(InvalidRoot)
 run_cmake_presets(NoVersion)